2025 г.

Подход к анализу помеченных данных в статическом анализаторе Svace

DOI: 10.15514/ISPRAS-2025-37(6)-53

1,2 И.С. Черемисенов, ORCID: 0009-0004-5492-6914 <cheremisenov@ispras.ru>
1 А.Е. Бородин, ORCID: 0000-0003-3183-9821 <alexey.borodin@ispras.ru>
1 А.Е. Волков, ORCID: 0000-0002-6043-5095 <volkov@ispras.ru>
1 М.В. Великанов, ORCID: 0009-0006-5270-2997 <mvelikanov@ispras.ru>

1 Институт системного программирования им. В.П. Иванникова РАН,

Россия, 109004, г. Москва, ул. А. Солженицына, д. 25.

2 Московский государственный университет им. М.В. Ломоносова,

Россия, г. Москва, Ленинские горы д.1.

Аннотация. Многие уязвимости программного обеспечения вызваны непроверенными данными, которые программы получают из небезопасных источников и используют затем в критических операциях. Обнаружение дефектов подобного рода можно свести к задаче анализа помеченных данных. В нашей работе мы обсуждаем необходимые свойства анализа для практического обнаружения в исходном коде реальных программных проектов ошибок, связанных с небезопасным использованием помеченных данных, и описываем подход, реализованный в рамках инструмента статического анализа Svace.

Ключевые слова: статический анализ; помеченные данные; анализ помеченных данных; ошибки программного обеспечения; анализатор Svace; C/C++; символьное выполнение; межпроцедурный анализ.

Для цитирования: Черемисенов И.С., Бородин А.Е., Волков А.Е., Великанов М.В. Подход к анализу помеченных данных в статическом анализаторе Svace. Труды ИСП РАН, том 37, вып. 6, часть 4, 2025 г., стр. 97–118. DOI: 10.15514/ISPRAS-2025-37(6)-53.

1. Введение

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

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

Задача анализа помеченных данных состоит в построении путей распространения помеченных данных между их возникновением (источниками – sources) и их использованием (приёмниками – sinks); в случае статического анализа – без запуска программы. Источниками и приёмниками являются некоторые предварительно заданные множества инструкций программы (например, вызовы определённых функций), дополнительно определяются способы их распространения и санитации [1].

Некоторые примеры эксплуатации подобных уязвимостей:

  • При использовании внешних данных для доступа к массиву может произойти переполнение буфера, что позволит злоумышленнику получить контроль над устройством.
  • При недостаточной проверке содержимого запроса к серверу злоумышленник может воспользоваться этим для подделки запросов со стороны сервера (SSRF, Server Side Request Forgery – подделка запросов со стороны сервера) или для вставки вредоносного кода в часть исполняемой на стороне сервера команды под видом обычных данных (OS Command Injection).

Подобные ошибки в классификации CWE (Common Weakness Enumeration) [2] относятся к категории CWE-707: Improper Neutralization [3], большей частью попадая в её подкатегории CWE-20: Improper Input Validation [4] и CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component (’Injection’) [5]. Ошибки из этих категорий входят в CWE Top-25 (25 наиболее опасных дефектов программного обеспечения по версии CWE) [6].

В нашей работе мы предлагаем комплексный подход к поиску таких ошибок с помощью средств статического анализа программ. Описываемые анализы и детекторы реализованы в статическом анализаторе Svace [7-9] для языков C/C++, Java, Kotlin, Scala, Go и Python.

2. Практические требования к анализатору для поиска ошибок использования помеченных данных

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

На практике используются всевозможные компромиссы. Одним из подходов является поиск приближенного решения. Возможны два крайних варианта приближенных решений:

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

При выборе первого варианта анализатор может пропустить реальные уязвимости. В силу критичности ошибок помеченных данных выбор столь строгого по отношению к ложным срабатываниям варианта для инструмента поиска ошибок нежелателен. Однако при выборе второго варианта анализатор может выдать чрезмерное количество ложных срабатываний, и при такой «шумной» выдаче предупреждений даже найденная анализатором реальная ошибка может остаться незамеченной программистом.

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

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

Рис. 1. Характеристики подходов к нахождению дефектов.

Рис. 1. Характеристики подходов к нахождению дефектов.

Например, на рис. 2 показан пример подобной подозрительной ситуации, которая не является гарантированно ошибочной, но которую лучше отсмотреть. Здесь источником ошибки является параметр функции, видимой извне модуля. В качестве параметра такой функции могут выступать непроверенные внешние данные, при попадании которых в строку запроса может появиться уязвимость вида «SQL-инъекция». Без контекста вызова неизвестно, точно ли параметр может приходить из ненадёжного источника без должной проверки, но обнаружение такой ситуации может представлять интерес для разработчиков, особенно в случае повышенных требований к безопасности разрабатываемого ПО.

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

public static void findUserByName(String username) throws Exception {

Connection conn = DriverManager.getConnection("jdbc:mysql://

localhost:3306/mydb", "user", "password");

Statement stmt = conn.createStatement();

String query = "SELECT * FROM users WHERE username = '" +

username + "'";

stmt.executeQuery(query);

}

Рис. 2. Пример возможной SQL-инъекции.

Распространение помеченных данных в программе, приводящее к ошибкам, может происходить и между модулями программы. Соответственно, для поиска таких ошибок необходим межпроцедурный анализ, способный отслеживать передачу данных между вызовами процедур1. Более того, большинство языков поддерживает косвенные вызовы функций, либо через указатели на функции, либо посредством виртуальных вызовов, при которых вызов конкретной функции зависит от типа объекта. Ввиду этого анализатор также должен поддерживать анализ разрешения косвенных вызовов. Такой анализ, в частном случае разрешения виртуальных вызовов и вызовов по указателю, также называется девиртуализацией.

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

В отдельных случаях требуется чувствительность к путям, позволяющая отсеять пути, зависящие от несовместных предикатов.

3. Анализатор Svace

Анализатор Svace осуществляет глубокий межпроцедурный потоково- и контекстно-чувствительный анализ программы. Его ключевые особенности:

  • Использование входного представления программы на основе SSA-формы 2.
  • Анализ внутри функции основан на анализе значений, при котором моделируется взаимодействие с памятью, отслеживается значения ячеек памяти и переменных. Большинство анализируемых свойств ассоциируется со значениями переменных.
  • Девиртуализация для разрешения косвенных вызовов функций (вызовов по указателю, виртуальных вызовов).
  • Все детекторы для анализируемой функции работают одновременно, что позволяет получить невысокую стоимость одного детектора.
  • Неконсервативный анализ, позволяющий обеспечить высокий процент истинных срабатываний при сравнительно невысоком времени работы.

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

Преимуществами такого подхода являются высокая скорость и масштабируемость. Каждая функция анализируется один раз, а резюме достаточно компактны, так как описывают только важные для анализа детали поведения функции. По этой причине анализ на основе резюме используется во многих инструментах статического анализа: Calysto [11], Saturn [12], Prefix [13], CSharpChecker [14] и др.

В анализаторе Svace можно выделить подготовительную фазу, включающую в себя:

  • Чтение спецификаций библиотечных функций 3, которые предоставляют анализатору их семантику для анализа их вызовов.
  • Построение графа вызовов.

Непосредственно анализ состоит из двух фаз:

  • Предварительная фаза – вспомогательная легковесная фаза перед основной.
  • Основная фаза анализа, на которой происходит построение резюме функций и поиск ошибок.

Девиртуализация в Svace реализована на предварительной фазе анализа [15]. При обходе модулей программы собирается статистика о вызовах функций. После обхода всех модулей собранные данные обрабатывается итеративным алгоритмом, который собирает зависимости и строит таблицы виртуальных вызовов. На основе результатов девиртуализации достраивается граф вызовов, в который добавляются рёбра, соответствующие косвенным вызовам.

4. Анализ на основе резюме

4.1 Детекторы «источник-приёмник»

Для анализа помеченных данных применяются детекторы типа «источник-приёмник». Их работа основана на отслеживании потока данных от точек их возникновения в источниках к приёмникам. В качестве источников описываются места возникновения данных, которые считаются потенциально небезопасными – как правило, это данные, поступающие в программу извне. Приёмники означают использования данных, которые могут приводить к реализации уязвимости.

Общая схема работы таких детекторов:

  • Данные, пришедшие из источника, «помечаются» и отслеживаются далее как помеченные.
  • Строятся возможные пути распространения помеченных данных, с учётом:
  • точек санитации – снятия помеченности;
  • точек пропагации – возникновения новых помеченных значений на основе использования уже имеющихся.
  • При попадании помеченных данных в приёмник фиксируется потенциальная уязвимость.
4.2 Распространение помеченных данных

На рис. 3 продемонстрирована классическая уязвимость SQL-инъекции. Программа формирует запрос, используя данные из внешнего источника без предварительной проверки, что позволяет злоумышленнику поместить в него вредоносный код.

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

При попадании query в вызов библиотечной функции исполнения SQL-запроса sqlite3_exec, второй параметр которой определен спецификацией как приёмник, детектор фиксирует уязвимость и создает предупреждение для пользователя.

void vulnerable_function() {

// Source: data from an environment variable

char *username = getenv("USER_NAME");

char query[256];

sqlite3 *db;

sqlite3_open(":memory:", &db);

// Building request with snprintf

snprintf(query, sizeof(query),

"SELECT * FROM users WHERE name = '%s';", username);

// Sink: SQL request execution (SQL injection vulnerability)

sqlite3_exec(db, query, 0, 0, 0);

sqlite3_close(db);

}

Рис. 3. CWE-89: SQL-инъекция.

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

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

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

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

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

4.2.1 Формулы достижимости

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

Анализатор проверяет совместность условий формул достижимости источника и приёмника. Для этого используется SMT-решатель. Если решатель подберёт значения переменных в формуле так, что она будет выполнима, то будет выдано предупреждение.

Пусть формула F1 зависит от переменной a: adef(F). Частичная функция tr трансляции формальных параметров в фактические аргументы для каждого формального параметра возвращает фактический аргумент. Тогда для трансляции формулы необходимо заменить переменную a на tr(a). Полученную формулу обозначим как F2=F1[atr(a)].

Не всегда для формального параметра есть соответствующий фактический аргумент. Например, для локальной переменной, использующейся для временных вычислений. В этом случае функция tr будет не определена для такого параметра.

В простом случае формула F1 содержит только одно атомарное условие, включающее переменную a один раз. В этом случае можно безопасно убрать это условие, т.к. SMT-решатель всегда сможет подобрать значение для этой переменной.

В более сложных случаях приходится выбирать компромисс между точностью и полнотой. Возможны следующие варианты:

  • Формула остаётся без изменений. Этот способ позволяет построить точные условия. Но их размер начинает разрастаться, и резюме уже перестают быть компактными, а анализ масштабируемым.
  • Атомарное условие удаляется из формулы. В этом случае возможна потеря точности. Если атомарное условие заменять на True («истина»), то результирующая формула будет чаще выполнимой. Если же заменять на False («ложь»), то, наоборот, формула будет реже выполнимой.

Чтобы не выдавать излишне много ложных срабатываний, в Svace атомарное условие в описанном случае заменяется на False. Как результат, при достаточно большом расстоянии от источника к приёмнику в графе вызовов растёт вероятность упрощения условий до ложных.

4.2.2 Информация о приёмниках

Информацию о разных приёмниках можно добавлять в виде дизъюнкции в результирующую формулу. Результирующая формула будет объединением формул о всех возможных приёмниках.

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

Отметим, что подобная проблема теоретически может возникать и для источников помеченных данных. Однако, как показывает практика, источников меньше приёмников.

5. Анализ сверху вниз на предварительной фазе

5.1 Межпроцедурная зависимость аргументов

Анализ на основе резюме может эффективно находить множества ошибок помеченных данных. Но из-за особенностей, описанных в разделах 4.2.1 и 4.2.2, не все ошибки будут найдены:

  • С ростом удаления функции с источником от приёмника на графе вызовов, вероятность пропуска ошибки повышается.
  • Для «сложной» для анализа методом резюме функции (по условиям или по количеству приёмников) повышается вероятность не сохранения приёмника в резюме.

Для решения проблемы «сложных» функций в Svace в дополнение к подходу на основе резюме используется анализ на основе графа зависимостей между формальными параметрами процедур. Этот граф строится на предварительной легковесной фазе анализа.

В этом графе каждая вершина представляет собой пару вида идентификаторфункции;$номерпараметра. Направленные рёбра графа соответствуют связи параметров различных процедур связаны между собой по потоку данных через них. Ребро от f1,$n1 к f2,$n2 означает, что параметр функции f1с номером n1 передаётся как аргумент с номером n2 функции f2. При этом учитывается не только непосредственная передача, но и зависимость через арифметические операции, копирование и другие преобразования.

Пример такого графа приведён на рис. 4. Например, значение параметра с номером 2 функции bar влияет в качестве слагаемого на результат сложения, передаваемый в функцию use как первый аргумент ($1), и передаётся напрямую в функцию log как второй аргумент ($2).

Рис. 4. Построение графа зависимостей аргументов и подграф помеченных аргументов.

Рис. 4. Построение графа зависимостей аргументов и подграф помеченных аргументов.

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

  • Для каждой анализируемой процедуры собирается информация о зависимости определяемых в ней значений от её параметров. Например, на рис. 5 значение переменной c зависит от формального параметра a.
  • Если такие значения используются в качестве аргументов вызова другой процедуры, анализ фиксирует зависимость формальных параметров вызывающей и вызываемой процедур.
  • Результатом анализа процедуры является добавленный отрезок (новое ребро и, при отсутствии – вершины) итогового графа.

Так, результату анализа процедуры foo, показанной на рис. 5, соответствуют ребра графа (на рис. 3 изображены жирными стрелками): ⟨foo; $1⟩ → ⟨bar; $2⟩,  ⟨foo; $2⟩ → ⟨bar; $1⟩,  ⟨foo; $2⟩ → ⟨process; $1⟩,  ⟨foo; $3⟩ → ⟨process; $3⟩

void somefunc(void) {

int v;

scanf("%d", &v);

bar(v);

}

void bar(int x, int y) {

use(x + y);

log("x: ", x);

}

void foo(int a, int b, const char *data) {

int c = a + 20;

bar(b, c);

process(b, stdout, data);

}

Рис. 5. Зависимость аргументов.

Полученный граф используется для отслеживания потока помеченных данных через параметры функций по графу вызовов. Для этого учитываются зависимости, определённые на предыдущем этапе: итоговый граф зависимостей, а также пары, соответствующие источникам помеченных данных, подаются на вход итеративному алгоритму, выполняющему обход направленного графа в глубину. Все вершины, достигнутые в процессе обхода, считаются помеченными и сохраняют трассу от исходного источника к каждой такой вершине. Например, в ходе анализа инструкций ещё какой-либо функции (например, такой как somefunc – непосредственно её нет в графе зависимостей аргументов, так как нет зависимостей по данным её аргументов), может стать известно, что в функцию bar в качестве первого аргумента передаются помеченные данные (например, внешние данные, считанные scanf), тогда вершина ⟨bar; $1⟩ графа считается источником помеченных данных. На рис. 4 двойным контуром выделены вершины подграфа, которые будут помечены в описанном случае.

Если на основной фазе анализа оказывается, что формальные параметры функции являются помеченными согласно графу зависимостей, то эта функция анализируется в специальном режиме: такие формальные параметры считаются источниками в рамках внутрипроцедурного анализа помеченных данных «источник-приёмник», при этом резюме функции не строится. Если в результате анализа функции определяется, что параметр может попасть в приёмник, то выдаётся предупреждение.

5.2 Логическая взаимосвязь аргументов

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

Можно заметить, что если буфер приходит в функцию parse из аргумента argv функции main (рис. 7), а индекс из её аргумента argc, то выхода за границы произойти не может.

typedef struct ArgData ArgData;

extern void addArgData(ArgData *data, int argc, char *argv[]);

void parseArgData(ArgData *data, int argc, char *argv[]) {

// . . .

if (argc > 1) {

addArgData(argv[argc - 1]); // access

}

}

Рис. 6. Возможное срабатывание детектора выхода за границы массива.

extern void log(const char str[]);

int main(int argc, char *argv[]) {

ArgData data;

//

log(argv[0]);

// . . .

parseArgData(&data, argc, argv);

// . . .

}

Рис. 7. Логическая взаимосвязь значений параметров функции main.

Рис. 8. Граф зависимостей аргументов.

Рис. 8. Граф зависимостей аргументов.

На рис. 8 показан граф межпроцедурной зависимости аргументов (см. 5.1), полученный в результате анализа кода из рис. 6 и 7. В этом графе информация о логической взаимосвязи значений аргументов между собой никак не отражена. Если использовать только этот граф, анализ на основной фазе будет считать, что значения аргументов получены из ненадёжного источника и без учёта их связи между собой это привело бы к выдаче ложного сообщения о дефекте.

Для описания дополнительных внутрипроцедурных логических взаимосвязей аргументов процедур между собой на предварительной фазе строится дополнительный граф. Как и в случае межпроцедурной зависимости, направленным рёбрам этого графа соответствует поток данных в программе по графу вызовов. Вершинами графа являются идентификаторы функций с дополнительными данными в виде пар номеров взаимосвязанных аргументов и указанием их ролей в этой взаимосвязи. Используются следующие роли: «DataSource» и «DataSize», означающие массив значений и его размер соответственно. Такой подход позволяет потенциально расширить этот механизм в случае необходимости на другие случаи связей между аргументами и их ролей в них, например: входной буфер и выходной, файловый

дескриптором и режим его использования и т.д.

Пример такого графа для кода с рис. 6 и 7 приведён на рис. 9.

Используя информацию из этого графа, анализ на основной фазе сможет определить, что значение argc соответствует размеру массива argv, и не станет сообщать об ошибке переполнения при обращении по индексу argc-1.

Рис. 9. Граф зависимостей взаимосвязанных аргументов с их ролями.

Рис. 9. Граф зависимостей взаимосвязанных аргументов с их ролями.

6. Отслеживание помеченных данных с учётом их вида

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

Cookie[] cookies = request.getCookies();

for (int i = 0; i < cookies.length; i++) {

Cookie c = cookies[i];

if (c.getName().equals("role")) { // FLAW

userRole = c.getValue();

}

}

Рис. 10. CWE-565: использование куки в потоке управления без валидации.

На рис. 10 показан пример уязвимости CWE-565, при которой программа опирается на данные из куки (cookie) при принятии решений. Злоумышленник может подменить или изменить содержимое куки, что открывает возможности для различных атак, включая подделку сессий и обход аутентификации.

Чтобы отследить использование куки в потоке управления, можно попытаться считать приёмником любой предикат. В случае нашего примера – это метод String.equals. Однако такой подход приведёт к огромному множеству ложнопозитивных срабатываний для других видов помеченных данных, потому что в общем случае передача помеченных данных в String.equals не представляет угрозы и даже напротив: может использоваться в качестве одной из проверок для их валидации.

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

В результате такой подход:

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

На рис. 11 показан пример кода на языке Java, в котором в два вызова метода URLConnection.connect попадает информация из запроса к серверу (метод doGet) и из переменной окружения (метод getProperty).

В первом случае информация, пришедшая из запроса, может содержать контролируемую пользователем ссылку. Сервер, используя её для подключения, уязвим к SSRF-атаке. Анализ отслеживает данные из такого источника как помеченные данные семантического типа REQUESTPARAM, что позволяет выдавать предупреждение соответствующего подтипа (TAINTED_PTR.SSRF), классифицируя обнаруженную ошибку как относящуюся
к CWE-918.

Во втором случае информация приходит из переменной окружения. Анализ отслеживает такие данные как семантический тип, соответствующий некоторому «абстрактному» (общему) виду семантических данных. Переменные окружения часто используются для хранения и задания конфигурации приложения, и, хотя такие данные представляют угрозу при неправильном использовании, эта ошибка не относится к CWE-918. Детектор сообщает об ошибке помеченных данных общего вида (TAINTED_PTR).

@GetMapping("/api/test")

protected void doGet(@RequestParam("url") String userInput) {

String configUrl = System.getProperty("INTERNAL_API");

URLConnection conn1 = new URL(userInput).openConnection();

conn1.connect(); // FLAW: CWE-918

URLConnection conn2 = new URL(configUrl).openConnection();

conn2.connect(); // FLAW: Uncategorized TAINTED_PTR

}

Рис. 11. Пример соотнесения типов помеченности источников и приёмников.

7. Механизмы установки, передачи и очистки помеченных данных

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

Для описания свойств библиотечных функций в Svace используются спецификации. Спецификация в Svace – это собственное определение функции, написанное на анализируемом языке и описывающее интересующие свойства поведения функции в компактной форме. Свойства указываются с помощью набора предопределённых функций (спецфункций), имеющих специальную семантику для анализатора.

7.1 Спецфункции для помеченных данных

В соответствии с вышеописанными механизмами реализованные в Svace спецфункции для описания работы с помеченными данными делятся на следующие четыре основные группы:

  • Источники помеченных данных. С помощью этих спецфункций можно обозначить переменную, значение которой анализ будет считать помеченным, а при необходимости указать и соответствующий этому значению семантический тип помеченных данных. Также можно указать дополнительную переменную, которая будет проверена на наличие чувствительных данных (пароли, токены, ключи авторизации, идентификаторы HTTP-сессии и т.п.). В этом случае помеченные данные возникнут, только если результаты проверки будут положительными.
  • Пропагаторы помеченных данных – спецфункции, описывающие распространение «помеченности». Такие спецфункции определяют входную и принимающую переменные, и в случае помеченности значения входной переменной значение принимающей переменной также считается помеченным.
  • Санитайзеры помеченных данных. Спецфункции, позволяющие указать переменную, значение которой перестаёт считаться помеченным. Также можно указать условие санитации в виде дополнительной булевой переменной. В этом случае помеченные данные будут устранены, только если значение этой переменной истинно.
  • Приёмники помеченных данных. С помощью этих спецфункций можно обозначить потенциально небезопасное использование помеченных значений. Также они позволяют установить тип приёмника, который в свою очередь определяет, на какие типы помеченных данных он реагирует и какой подтип предупреждения TAINTED_PTR будет выдан при их наличии. Поскольку некоторые библиотечные функции могут быть подвержены сразу нескольким уязвимостям, эти спецфункции дают возможность указать произвольное количество типов приёмника помеченных данных.

На рис. 12 в качестве примера использования спецфункций для описания работы с помеченными данными приведена спецификация метода execute класса java.sql.Statement. Как известно, методы этого класса, выполняющие SQL-запросы, не используют экранирование специальных символов, которые могут содержаться в запросах, поэтому их вызовы считаются уязвимыми для SQL-инъекций. Это поведение отражено в спецификации при помощи вызова спецфункции sf_set_trusted_sink_ptr_with_type, описывающей небезопасное использование помеченных данных, передаваемых в execute через аргумент sql (SQL-запрос для выполнения). Второй аргумент вызова (строковый литерал ”SQLINJECTION”) определяет тип приёмника для этих помеченных данных. Тип приёмника, соответствующий данному аргументу, определен в анализаторе как реагирующий на все типы помеченных данных и приводящий к выдаче предупреждения TAINTED_PTR.SQL_INJECTION.

public boolean execute(String sql) throws SQLException {

SpecFunc.sf_set_trusted_sink_ptr_with_type(sql, "SQLINJECTION");

return SpecFunc.sf_get_some_boolean();

}

Рис. 12. Использование спецфункций для помеченных данных в спецификациях.

С целью более точного описания поведения некоторых специфических библиотечных функций в дополнение к этим основным группам была реализована группа спецфункций для преобразования помеченных данных. По сути, эти спецфункции есть частный случай спецфункций для пропагации помеченных данных с той лишь разницей, что они позволяют указать либо целевой тип помеченных данных для значения принимающей переменной (не совпадающий с типом помеченных данных на входной переменной), либо другое моделируемое анализом свойство принимающей переменной на основе помеченности её входных данных. Так, например, для моделирования поведения функций strchr и strrchr стандартной библиотеки C использована спецфункция, преобразующая помеченные данные любого типа, установленные на её первом аргументе (строка для поиска) в свойство возвращаемого значения функции, обозначающее, что это значение может быть нулевым. Если возвращаемое значение далее в коде будет разыменовано, это приведет к выдаче соответствующего предупреждения с указанием всех событий на пути распространения данных – от возникновения помеченных данных до их преобразования в нулевое значение и далее до его точки разыменования.

8. Формальные параметры как источник помеченных данных

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

Как было показано в разделе 5.1, даже если информация об источниках и приёмниках в программе полная, анализатор может упрощать условия пути в целях масштабируемости анализа. Чтобы отслеживать подозрительные использования данных в уязвимых приёмниках таких процедур, полезно использовать некоторую эвристику, которая поможет увеличить полноту анализа, не ухудшив при этом результаты остального анализа помеченных данных.

Идея такой эвристики проста: будем считать источником помеченных данных значения формальных параметров таких процедур, которые подходят под понятие «открытые» – то есть процедур, которые потенциально могут быть использованы вне модуля программы, самой программы или библиотеки. Для каждого языка важно установить свои определения открытых процедур. Так, например, для языков C/C++ таковыми логично полагать функции с внешним связыванием (external linkage) – такие функции могут быть использованы вне объектного файла, содержащего их определение.

Мы считаем процедуру по умолчанию открытой, если она не подпадает под одно из следующих условий:

  • функции внутренней линковки в языке C – обычно помечаются спецификатором static;
  • приватные и статические методы классов, любые методы закрытых классов или классов, находящихся в приватных пространствах имён, а также любые конструкторы в языках Java, Kotlin и C++;
  • внутренние методы языков Python и Go – начинающиеся со строчной буквы и символа подчёркивания («_») соответственно.

Анализ помечает аргументы таких функций атрибутом TaintedPtr со специальным типом ARG.

Таким образом, предложенный механизм выдачи предупреждений выглядит так: анализатор находит потенциально уязвимый для атак (открытый) метод. Все его формальные параметры автоматически считаются источниками помеченных данных. Используя стандартный анализ помеченных данных, отслеживается их перемещение к приёмнику, включая все проверки, условия и санитации. Попадание в любой допустимый приёмник приведёт к выдаче предупреждения TAINTED_PTR.ARG, игнорируя установленный тип приёмника.

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

Чтобы отделить результаты анализа на основе описанной эвристики от остального анализа помеченных данных мы установили ограничения на распространение помеченных данных типа ARG:

  • Информация о пометке таких данных не должна сохраняться в резюме функции, что исключит её распространение вверх по графу вызовов. Важно отметить, что, несмотря на это, межпроцедурность такого подхода всё ещё сохраняется – информация о пометке применяется к резюме вызываемых процедур.
  • Атрибут типа ARG можно интерпретировать как наименьший элемент в решетке типов. Это будет означать, что для любого типа помеченных данных x, отличного от ARG, справедливо:
ARGtypex=x
  • Напоследок отметим, что для помеченных данных данного типа установлен определенный набор допустимых приёмников. Другие могут быть слишком специфичны для типа ARG, что приведет к большому количеству ложных или бесполезных предупреждений.

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

9. Результаты

9.1 Реализованные подтипы предупреждения TAINTED_PTR

Поддержка отслеживания помеченных данных с учётом видов их источников и приёмников позволило разделить выдаваемые Svace предупреждения типа TAINTED_PTR на подтипы, что позволяет пользователю легче ориентироваться во множестве результатов анализа, фокусируясь на устранении тех классов уязвимостей, которые являются критичными для конкретного анализируемого проекта. Кроме того, это обеспечивает сопоставление результатов анализа с типами уязвимостей CWE, что может быть критичным для обеспечения соответствия разрабатываемого ПО международным стандартам безопасности – таким как OWASP ASVS, ISO 27001 и другим. Подтипы предупреждения TAINTED_PTR и их соответствие CWE приведены в табл. 1.

Для проверки работоспособности анализа помеченных данных использовались три открытых проекта различного масштаба и назначения:

  • Android 14 [16] – полнофункциональная мобильная операционная система, содержащая десятки миллионов строк кода. Представляет собой пример крупного индустриального проекта с разнообразной функциональностью.
  • WebGoat [17] – учебное приложение, намеренно содержащее широкий набор уязвимостей. Используется для обучения методам их поиска и эксплуатации.
  • Juliet Test Suite [18] – набор изолированных тестов, разработанный для верификации инструментов статического анализа. Включает большое количество шаблонов типовых уязвимостей, представленных в стандартизированной форме.

Табл. 1. Подтипы предупреждения TAINTED_PTR и их соответствие CWE.

Подтип TAINTED_PTR.*CWEОписание
.COOKIE565Cookie Spoofing
.COOKIE.INSECURE315, 614Sensitive Information in Insecure Cookie
.SSRF918Server-Side Request Forgery
.SQL_INJECTION89SQL Injection
.LDAP_INJECTION90LDAP Injection
.JWT347Improper Verification of Cryptographic Signature
.XSS80, 83Cross-Site Scripting, XSS
.HTTP_RESPONSE_SPLIT113HTTP Response Splitting
.CONFIG_CONTROL15External Control of System or
Configuration Settings
.PATH_TRAVERSAL23, 36Relative/Absolute Path Traversal
.REFLECTION470Unsafe Reflection
.REDIRECT601Redirection to Untrusted Site
.XXE611Improper Restriction of XML External
Entity Reference
.FORMAT_STRING134Externally-Controlled Format String
.LOG_FORGING117Improper Output Neutralization for Logs
.ARGУязвимости с источником – формальным аргументом (см. раздел 8)
TAINTED_PTRОбщий случай передачи недостоверных
данных в уязвимый источник

Поскольку анализ помеченных данных тесно интегрирован с остальной логикой анализа и не может быть выделен в отдельный этап, измерения времени проводились для всего процесса анализа целиком. Результаты приведены в табл. 2.

Дополнительно в табл. 3 представлены количественные характеристики по обнаруженным предупреждениям для двух проектов, а также доля подтверждённых срабатываний, отражающая точность инструмента на практике. Сравнительно небольшое количество найденных на Android 14 дефектов обусловлено их критичностью: разработчики уделяют им особое внимание, не оставляя возможности их эксплуатации в финальном продукте.

Для количественной оценки качества реализованных детекторов был использован набор Juliet Test Suite. В табл. 4 приведены значения полноты обнаружения (recall) по ключевым категориям CWE на момент проведения экспериментов.

Табл. 2. Сравнение времени анализа и объёма кода для различных проектов.

ПроектВремя анализа (мин)Объем кода (строки)
Android 148012 млн
WebGoat220 тыс
Juliet Test Suite26500 тыс

Табл. 3. Обнаруженные предупреждения по типам уязвимостей.

Подтип TAINTED_PTR.WebGoatAndroid 14Истинных (%)
.ARG735583
.COOKIE7100
.PATH_TRAVERSAL110100
.SQL_INJECTION11100
.SSRF1100
.REFLECTION20100

Табл. 4. Полнота обнаружения уязвимостей по категориям CWE.

CWEНазваниеПолнота (%)
CWE-113HTTP Response Splitting44.44
CWE-15Config Control72.97
CWE-23Path Traversal (rel.)59.46
CWE-36Path Traversal (abs.)46.62
CWE-470Unsafe Reflection48.20
CWE-78OS Command Injection84.23
CWE-80XSS (Basic)45.65
CWE-81XSS (Error Msg)45.65
CWE-83XSS (Attribute)45.65
CWE-89SQL Injection37.12
CWE-90LDAP Injection48.20
Итого52.56
9.2 Примеры найденных уязвимостей

Рис. 13 демонстрирует пример уязвимости подделки запросов со стороны сервера (Server-Side Request Forgery, SSRF), обнаруженной в открытом проекте WebGoat на языке Java. Уязвимость заключается в небезопасном использовании внешнего ввода, поступающего через HTTP-параметр url, для установления сетевого соединения на стороне сервера.

Метод completed обрабатывает POST-запрос по маршруту /SSRF/task2 и принимает параметр url, аннотированный с помощью @RequestParam, что указывает на его происхождение из запроса клиента. Полученное значение передаётся в метод furBall, в котором выполняется его дальнейшая обработка.

В теле метода furBall реализована попытка фильтрации допустимых значений URL с использованием регулярного выражения, однако такая проверка является ненадёжной. При совпадении строки url с шаблоном http://ifconfig\\.pro происходит непосредственное открытие потока по заданному адресу через вызов new URL(url).openStream() (строка в коде отмечена как FLAW). Подобная конструкция потенциально позволяет злоумышленнику подставить адрес, удовлетворяющий регулярному выражению, но ведущий к внутренним сервисам, что открывает возможность SSRF-атаки. Более того, отсутствие явного списка разрешённых хостов и недостаточная нормализация URL делают фильтрацию уязвимой к обходу.

@PostMapping("/SSRF/task2")

@ResponseBody

public AttackResult completed(@RequestParam String url) {

return furBall(url);

}

protected AttackResult furBall(String url) {

if (url.matches("http://ifconfig\\.pro")) {

String html;

try (InputStream in = new URL(url).openStream()) { // FLAW

html =

new String(in.readAllBytes(), StandardCharsets.UTF_8)

.replaceAll("\n", "<br>");

} catch (MalformedURLException e) {

// <...>

}

return success(this)

.feedback("ssrf.success")

.output(html)

.build();

}

var html = "<img src=\"images/cat.jpg\">";

return getFailedResult(html);

}

Рис. 13. Уязвимость вида SSRF на проекте WebGoat.

10. Схожие работы

Для поиска уязвимостей помеченных данных также широко применяются техники динамического анализа [19]. Существенным преимуществом динамического анализа является отсутствие ложных срабатываний. Статический анализ при этом имеет лучшее покрытие редко используемых путей.

Подход IFDS [20] сводит задачу поиска помеченных данных к задаче достижимости на графе. Этот подход используется инструментом Irbis [1], который реализует статический межпроцедурный анализ помеченных данных на основе IFDS для C/C++. Из-за отсутствия чувствительности к путям анализатор имеет более высокий процент ложных срабатываний. В инструменте также реализован анализ косвенных вызовов. В отличие от подхода Svace, в Irbis при наличии нескольких кандидатов все они добавляются в граф вызовов. Благодаря этому можно достичь ещё более высокое покрытие. На рис. 1 инструмент находится по отношению к Svace левее и выше.

В легковесном статическом анализаторе Splint [21] для поиска уязвимостей используются компромиссные решения для того, чтобы найти уязвимости, при этом жертвуя полнотой и точностью анализа. Инструмент ограничивает анализ потока данных границами процедур (возможно, это объясняется тем, что его разработали в 2021 году), для анализа вызов процедур используются аннотации в коде, вручную добавляемые программистом. Для аннотирования семантики процедур используются комментарии в коде. При этом аннотировать можно не только определения функций, но и их объявления, что позволяет аннотировать библиотечные функции с отсутствующим кодом. Такие аннотации являются альтернативой спецификациям Svace. Вместе с тем, благодаря наличию межпроцедурного анализа, Svace может значительно лучше распространять информацию о программе, из-за чего требуется меньше усилий от программиста. Инструмент так же, как и Svace, использует технику для объединения состояний программы в точках слияния.

Инструмент Splint предоставляет аннотации tainted и untainted для строковых переменных. Инструмент выдаст предупреждение, если значение, помеченное аннотацией tainted, будет передано в функцию, аргумент которой помечен как untainted. Интересно, что пользователь может сам определять правила распространения помеченных данных для своих атрибутов, включая правила объединения на путях слияния.

В работе [22] используют вывод типов для поиска уязвимостей, связанных с использованием непроверенных данных в качестве строки формата. Помеченные данные реализованы в виде расширения системы типов С дополнительными квалификаторами типов. Квалификатор tainted используется подобно уже существующему квалификатору const. Предупреждение выдаётся, если выражение с таким квалификатором используется как строка формата. На наш взгляд, это удачное применение квалификаторов. Но вместе с тем подход может плохо переноситься на другие виды уязвимостей, где требуется чувствительность к потоку управления и путям. Анализ типов по сути является потоково-нечувствительным видом статического анализа. Его применение там, где важно учитывать порядок инструкций, приведёт к излишнему количеству ложных срабатываний.

В работе [23] описаны анализы помеченных данных, реализованные в анализаторе Svace к 2021 году. В анализаторе не было разделения типов источников и приёмников, следствием чего было значительно меньше типов предупреждений, а также меньшее количество находимых дефектов.

Чтобы оценить качество результатов Svace в сравнении с некоторыми другими инструментами, приводим табл. 5.

Табл. 5. Сравнительные результаты работы разных инструментов на проекте OpenSSL.

анализаторсрабатыванийистинных (%)время (мин)
Irbis39961287
Svace7113100
CSA22148
Infer1517100

11. Заключение

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

Мы описали совокупность подходов и методов для построения такого анализатора, позволяющую отвечать этим требованиям, реализованную рамках инструмента статического анализа Svace для языков C/C++, Java, Kotlin, Scala, Go и Python. Основная часть анализа построена на основе модели «источник-приёмник» и использует метод резюме для межпроцедурного анализа, что позволяет, несмотря на пропуск части ошибок, достичь высокого процента истинных срабатываний и приемлемую скорость работы.

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

Примечания

  1. В работе мы будем использовать термины функция и процедура как синонимы.
  2. SSA — Static Single-Assignment form — представление программы, в котором каждой её переменной значение присваивается только один раз.
  3. Спецификации большинства функций стандартных библиотек входят в дистрибутив Svace, пользователи имеют возможность добавлять свои.

Список литературы

  1. Н. В. Шимчик, В. Н. Игнатьев и А. А. Белеванцев. Irbis: статический анализатор помеченных данных для поиска уязвимостей в программах на C/C++. Труды Института системного программирования РАН, 34(6):51–66, 2022. ↩1 ↩2
  2. MITRE Corporation. Common Weakness Enumeration (CWE). https://cwe.mitre.org/. Дата обращения: 2025-05-01.
  3. MITRE Corporation. Common Weakness Enumeration (CWE) – CWE-707: Improper Neutralization. https://cwe.mitre.org/data/definitions/707.html. Дата обращения: 2025-05-01.
  4. MITRE Corporation. Common Weakness Enumeration (CWE) – CWE-20: Improper Input Validation. https://cwe.mitre.org/data/definitions/20.html. Дата обращения: 2025-05-01.
  5. MITRE Corporation. Common Weakness Enumeration (CWE) – CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component (’Injection’). https://cwe.mitre.org/data/definitions/74.html. Дата обращения: 2025-05-01.
  6. MITRE Corporation. 2024 CWE Top 25 Most Dangerous Software Weaknesses. https://cwe.mitre.org/top25/archive/2024/2024_cwe_top25.html. Дата обращения: 2025-05-01.
  7. V. Ivannikov, A. Belevantsev, A. Borodin, V. Ignatiev, D. Zhurikhin и A. Avetisyan. Static analyzer svace for finding defects in a source program code. Programming and Computer Software, 40(5):265–275, 2014.
  8. A. Belevantsev, A. Borodin, I. Dudina, V. Ignatiev, A. Izbyshev, S. Polyakov и D. Zhurikhin. Design and development of Svace static analyzers. In 2018 Ivannikov Memorial Workshop (IVMEM):3–9, 2018.
  9. A. Borodin и I. Dudina. Intraprocedural Analysis Based on Symbolic Execution for Bug Detection. Programming and Computer Software, 47(8):858–865, 2021.
  10. W. Landi. Undecidability of static analysis. ACM Letters on Programming Languages and Systems (LOPLAS), 1(4):323–337, 1992.
  11. D. Babic и A. J. Hu. Calysto: scalable and precise extended static checking. В Proceedings of the 30th international conference on Software engineering, страницы 211–220, 2008.
  12. A. Aiken, S. Bugrara, I. Dillig, T. Dillig, B. Hackett и P. Hawkins. An overview of the saturn project. В Proceedings of the 7th ACM SIGPLAN-SIGSOFT workshop on Program analysis for software tools and engineering, страницы 43–48, 2007.
  13. W. R. Bush, J. D. Pincus и D. J. Sielaff. A static analyzer for finding dynamic programming errors. Software-Practice and Experience, 30(7), 2000.
  14. В. К. Кошелев, В. Н. Игнатьев и А. И. Борзилов. Инфраструктура статического анализа программ на языке C. Труды Института системного программирования РАН, 28(1):21–40, 2016.
  15. A. Galustov, A. Borodin и A. Belevantsev. Devirtualization for static analysis with low level intermediate representation. В 2022 Ivannikov Ispras Open Conference (ISPRAS), страницы 18–23. IEEE, 2022.
  16. Google LLC. Android open source project. https://source.android.com/, 2024. Дата обращения: 2025-05-01.
  17. OWASP Foundation. Owasp webgoat project. https://owasp.org/www-project-webgoat/, 2023. Дата обращения: 2025-05-01.
  18. National Security Agency. Juliet test suite for java. https://samate.nist.gov/SRD/testsuite.php, 2013. Дата обращения: 2025-05-01.
  19. J. Newsome и D. X. Song. Dynamic taint analysis for automatic detection, analysis, and signaturegeneration of exploits on commodity software. В NDSS, том 5, страницы 3–4. Citeseer, 2005.
  20. E. Bodden. Inter-procedural data-flow analysis with ifds/ide and soot. В Proceedings of the ACM SIGPLAN International Workshop on State of the Art in Java Program analysis, страницы 3–8, 2012.
  21. D. Evans и D. Larochelle. Improving security using extensible lightweight static analysis. IEEE software, 19(1):42–51, 2002.
  22. U. Shankar, K. Talwar, J. S. Foster и D. Wagner. Detecting format string vulnerabilities with type qualifiers. В 10th USENIX Security Symposium (USENIX Security 01), 2001.
  23. A. Borodin, A. Goremykin, S. Vartanov и A. Belevancev. Searching for tainted vulnerabilities in static analysis tool svace. Proceedings of the Institute for System Programming of the RAS, 33(1):7–32, 2021.

Информация об авторах

Иван Сергеевич Черемисенов – студент факультета вычислительной математики и кибернетики МГУ имени М. В. Ломоносова, сотрудник Института системного программирования РАН. Сфера научных интересов: компиляторные технологии, статический анализ.

Алексей Евгеньевич БОРОДИН – кандидат физико-математических наук, старший научный сотрудник ИСП РАН. Сфера научных интересов: статический анализ исходного кода программ для поиска ошибок.

Александр Ефимович Волков – старший научный сотрудник Института системного программирования РАН. Сфера научных интересов: статический анализ исходного кода программ для поиска ошибок.

Михаил Вадимович Великанов – ведущий программист Института системного программирования РАН. Сфера профессиональных интересов: разработка систем статического анализа исходного кода программ.

Связь с редакцией