Введение

Процесс анализа поведения компьютерных программ

В информатике анализ программ — это процесс автоматического анализа поведения компьютерных программ с точки зрения таких свойств, как корректность, устойчивость, безопасность и живость. Анализ программ фокусируется на двух основных областях: оптимизации программ и проверке корректности программ. Первая направлена на повышение производительности программы и снижение потребления ресурсов, а вторая — на обеспечение того, чтобы программа выполняла то, для чего предназначена. Анализ программ может выполняться без запуска программы (статический анализ программ), во время выполнения (динамический анализ программ) или в комбинации обоих подходов.

Статический анализ программы

В контексте корректности программы статический анализ может выявлять уязвимости на этапе разработки. Эти уязвимости проще исправить, чем те, которые обнаруживаются на этапе тестирования, поскольку статический анализ позволяет определить первопричину уязвимости. Поскольку многие формы статического анализа являются вычислительно неразрешимыми, механизмы для их реализации не всегда приводят к верному результату: они могут выдавать ложноотрицательные результаты («проблемы не найдены», хотя в коде они есть) или ложноположительные, либо никогда не выдавать неверный результат, но иногда не завершаться вовсе. Несмотря на эти ограничения, механизмы первого типа могут снизить количество уязвимостей, а второго – иногда давать уверенную гарантию отсутствия определенного класса уязвимостей. Некорректные оптимизации крайне нежелательны. Поэтому в контексте оптимизации программ существуют две основные стратегии обработки вычислительно неразрешимого анализа:

Оптимизатор, который должен завершиться за относительно короткое время, например, оптимизатор в оптимизирующем компиляторе, может использовать усеченную версию анализа, гарантированно завершающуюся за конечное время и находящую только корректные оптимизации. Инструмент оптимизации стороннего разработчика может быть реализован таким образом, чтобы никогда не выдавать некорректную оптимизацию, но при этом в некоторых ситуациях продолжать выполняться неопределенно долго, пока не найдет ее (что может и не произойти). В этом случае разработчику, использующему инструмент, придется остановить его и избегать повторного запуска на данном фрагменте кода (или, возможно, изменить код, чтобы избежать проблем с инструментом). Однако существует и третья стратегия, применимая к языкам с неполной спецификацией, например, к C. Оптимизирующий компилятор вправе генерировать код, который делает что угодно во время выполнения, включая аварийное завершение, если он сталкивается с исходным кодом, семантика которого не определена используемым стандартом языка.

Контрольный поток

Целью анализа потока управления является получение информации о том, какие функции могут быть вызваны в различных точках во время выполнения программы. Собранная информация представляется в виде графа потока управления (CFG), где узлами являются инструкции программы, а ребра – поток управления. Выделяя блоки кода и циклы, CFG становится основой для оптимизаций, выполняемых компилятором.

Анализ потока данных

Анализ потока данных — это техника, предназначенная для сбора информации о значениях в каждой точке программы и об их изменении с течением времени. Эта техника часто используется компиляторами для оптимизации кода. Одним из наиболее известных примеров анализа потока данных является проверка на загрязнение (taint checking), которая заключается в рассмотрении всех переменных, содержащих данные, предоставленные пользователем, как "загрязнённые", то есть небезопасные, и предотвращении использования этих переменных до их очистки. Эта техника часто используется для предотвращения атак SQL-инъекций. Проверка на загрязнение может выполняться статически или динамически.

Абстрактная интерпретация

Абстрактная интерпретация позволяет извлекать информацию о возможном выполнении программы, не осуществляя её фактическое выполнение. Эта информация может использоваться компиляторами для поиска потенциальных оптимизаций или для подтверждения соответствия программы определенным классам требований безопасности.

Системы типа

Типовые системы связывают типы с программами, удовлетворяющими определенным требованиям. Их цель — выбрать подмножество программ языка, которые считаются корректными согласно заданному свойству. Проверка типов — это установление, соответствует ли программа требованиям типовой системы. Проверка типов используется в программировании для ограничения способов использования программных объектов и их возможностей. Эту проверку выполняет компилятор или интерпретатор. Проверка типов также может помочь предотвратить уязвимости, гарантируя, что знаковое значение не будет присвоено беззнаковой переменной. Проверка типов может выполняться статически (на этапе компиляции), динамически (во время выполнения) или комбинированным способом. Статическая информация о типах (выводимая или явно указанная с помощью аннотаций типов в исходном коде) также может использоваться для оптимизаций, например, для замены массивов с упаковкой на массивы без упаковки.

Систем воздействия

Системы эффектов — это формальные системы, предназначенные для представления изменений, которые может вызвать выполнение функции или метода. Эффект кодифицирует, какое действие выполняется и над чем оно выполняется, что обычно называют видом эффекта и областью эффекта соответственно.

Проверка модели

Проверка модели — это строгие, формальные и автоматизированные методы проверки соответствия модели (в данном контексте — формальной модели фрагмента кода, хотя в других контекстах это может быть модель аппаратного обеспечения) заданной спецификации. Благодаря конечному числу состояний, присущему коду, и возможности представления как спецификации, так и кода в виде логических формул, можно проверить, нарушает ли система спецификацию, используя эффективные алгоритмические методы.

Динамический анализ программы

Динамический анализ может использовать информацию, полученную во время выполнения программы, для повышения точности анализа, а также обеспечивать защиту в процессе выполнения, однако он способен анализировать только один конкретный запуск и может снизить производительность программы из-за проверок, выполняемых во время работы.

Испытания

Программное обеспечение должно быть протестировано для обеспечения его качества и надежной работы, а также для предотвращения конфликтов с другим программным обеспечением, которое может функционировать одновременно с ним. Тестирование выполняется путем запуска программы с заданными входными данными и оценки ее поведения и выходных результатов. Даже если требования к безопасности явно не указаны, необходимо проводить дополнительное тестирование безопасности, чтобы убедиться, что злоумышленник не сможет изменить программное обеспечение, похитить информацию, нарушить его нормальную работу или использовать его для атак на пользователей.

Мониторинг

Программа мониторинга записывает и регистрирует различные виды информации о работе программы, такие как использование ресурсов, события и взаимодействия, для последующего анализа и выявления причин нештатного поведения. Кроме того, она может использоваться для проведения аудитов безопасности. Автоматизированный мониторинг программ иногда называют верификацией во время выполнения.

Разрезка программы

Для заданного подмножества поведения программы, выделение фрагмента программы (program slicing) заключается в приведении программы к минимальной форме, сохраняющей выбранное поведение. Полученная программа называется "фрагментом" и является точным представлением исходной программы в области заданного подмножества поведения. В общем случае, поиск фрагмента – неразрешимая задача, однако, если целевое поведение задано значениями набора переменных, можно получить приближенные фрагменты с помощью алгоритма анализа потока данных. Разработчики обычно используют эти фрагменты при отладке для определения источника ошибок.