Введение 4
Постановка задачи 7
1. Обзор предметной области 8
1.1. Существующие решения 8
1.1.1 SpotBugs 8
1.1.2 Coverity 8
1.1.3 CodeQL 9
1.1.4 Infer 10
1.1.5 Выводы 10
1.2. Символьное исполнение 11
1.2.1 Описание алгоритма 11
1.2.2 Пример работы 12
1.3. UnitTestBot 15
1.3.1 Архитектура 15
1.3.2 Пример работы 17
1.4. Taint-анализ 20
1.4.1 Описание алгоритма 20
1.4.2 Пример работы 22
2. Создание отчёта по сгенерированным тестам 24
2.1. Формат SARIF 24
2.2. Извлечение информации из тестов 26
2.3. Пример работы 27
3. Плагин для IntelliJ IDEA 30
3.1. Детали реализации 30
3.2. Пример работы 31
4. Taint-анализ 33
4.1. Конфигурация 33
4.2. Детали реализации 36
4.2.1 Общие концепции 36
4.2.2 Модификация символьной машины 37
4.2.3 Модификация генератора кода 40
4.3. Пример работы 40
5. Тестирование и эксперименты 43
5.1. Тестирование taint-анализа 43
5.2. Запуск на реальных проектах 45
5.2.1 Tape 45
5.2.2 Fastjson 46
5.3. Запуск на задачах Codeforces 47
Заключение 50
Список литературы 51
Приложение 55
Сложно представить себе разработку программного обеспечения в современном мире без применения инструментов анализа кода. По мере того как программы становятся сложнее, возрастает и сложность их тестирования, а значит возрастает и потребность в автоматизации поиска ошибок или дефектов. Одним из подходов к решению этой задачи является статический анализ.
Статический анализ — это анализ программного обеспечения, производимый (в отличие от динамического анализа [1]) без реального выполнения исследуемых программ. Чаще всего анализ производится над исходным кодом продукта, хотя иногда задействуется и какой-нибудь вид объектного кода, например, байт-код языка программирования Java. Таким образом, методы статического анализа помогают найти ошибки в коде ещё на этапе компиляции, что значительно ускоряет разработку программ, снижая потраченное программистом время на отладку и тестирование.
В связи с высокой востребованностью темы автоматического обнаружения дефектов в программах, на рынке существует огромное количество различных статических анализаторов. Список из наиболее известных инструментов, позволяющих анализировать Java код включает в себя SpotBugs [2], Coverity [3], CodeQL [4], Infer [5], а также многие другие. Перечисленные инструменты неплохо справляются с поиском простых ошибок или опечаток, однако в большинстве своём, не выполняют глубокого исследования кода, то есть не обнаруживают редкие сценарии исполнения, приводящие к некорректному поведению программы. Кроме того, часто допускается большое количество ложноположительных срабатываний. Другими словами, инструмент может выдать предупреждение о найденной ошибке, которой на самом деле не существует. Это, конечно же, негативно сказывается на производительности программиста, который теперь вынужден тратить время на рассмотрение выданного предупреждения.
Таким образом, существует запрос на разработку инструмента, который бы выполнял глубокий анализ и находил серьезные проблемы в коде, приводящие к неожиданному поведению программы. В достижении этой цели может помочь символьное исполнение.
Техника символьного исполнения заключается в том, чтобы исполнять программу не на конкретных значениях входных данных, а на так называемых символьных переменных. Благодаря этому, для каждого пути исполнения можно получить логические ограничения на значения входных параметров, при выполнении которых этот путь достигается. После этого при помощи SMT1-решателя такие значения могут быть найдены, и сгенерирован тестовый случай, реализующий данный путь. Описанный механизм обладает таким свойством, как теоретически полное покрытие кода, что позволяет обнаруживать редкие сценарии исполнения, которые приводят к неправильному результату работы программы. Ещё одно немаловажное достоинство символьного исполнения заключается в сравнительно небольшом количестве ложных срабатываний относительно других методов статического анализа [6].
Для оценки качества анализатора кода обычно используется реестр самых опасных уязвимостей программного обеспечения [7]. Список включает в себя такие ошибки как запись или чтение вне границ массива, SQL-инъекция, использование непроверенного пользовательского ввода, разыменование нулевого указателя, а также многие другие уязвимости, позволяющие злоумышленникам полностью захватить систему, украсть данные или помешать работе приложений. Символьное исполнение способно находить далеко не все из перечисленных ошибок, однако его возможности могут быть существенно расширены с помощью метода taint-анализа. Метод заключается в отслеживании распространения непроверенных внешних данных по программе и позволяет обнаруживать нарушения в безопасности, например, инъекции в базу данных, операционную или файловую систему. Таким образом, если встроить taint- анализ непосредственно в ядро символьной виртуальной машины, то можно объединить достоинства каждого из подходов, а именно, получить анализатор с низким уровнем ложноположительных срабатываний, который, помимо всего прочего, умеет находить проблемы в безопасности.
В рамках данной работы в качестве символьной виртуальной машины был взят UnitTestBot [8] — инструмент, который по исходному коду на языке Java автоматически генерирует тесты, пытаясь максимизировать покрытие. UnitTestBot может также находить тестовые случаи, приводящие к таким дефектам в программном обеспечении, как выбрасывание необработанного исключения, переполнения примитивных численных типов, зависания методов и некоторые другие. Однако сами по себе найденные значения входных параметров тестируемой функции ещё не являются приемлемым результатом статического анализа, поскольку качество анализатора измеряется не только в его способности находить неочевидные сценарии исполнения, но и в удобстве использования для программиста. Следовательно, выдаваемое предупреждение о найденной ошибке, помимо краткого описания самой ошибки, должно содержать конкретное место в коде, а так же всю ту информацию, которая может помочь пользователю разобраться в проблеме как можно быстрее.
Немаловажным фактором удобства использования инструмента является простота его установки и запуска. В современном мире большинство программистов используют интегрированные среды разработки (англ. IDE), которые дают возможность встраивать в себя сторонние модули для решения каких-либо задач. Например, UnitTestBot имеет собственный плагин для IntelliJ IDEA [9] — одной из наиболее популярных IDE для языка Java. В связи с этим было решено добавить функциональность выполнения статического анализа, а также отображения его результатов непосредственно в плагин UnitTestBot.
Основным результатом работы является готовый к использованию инструмент статического анализа кода на языке Java. В процессе достижения этой цели были выполнены следующие задачи.
• Проведён обзор существующих на рынке решений, в ходе которого были выявлены их ключевые недостатки — поверхностность проводимого анализа, а также большое количество ложноположительных срабатываний.
• Реализован модуль создания отчётов в формате SARIF на основе тестовых случаев, сгенерированных инструментом UnitTestBot.
• Разработан удобный пользовательский интерфейс для просмотра отчётов SARIF в среде разработки IntelliJ IDEA.
• В символьную виртуальную машину встроена техника taint-анализа, расширяющая её возможности. Реализованный алгоритм позволяет находить нарушения безопасности, а именно, случаи использования непроверенных данных в критических секциях программы.
• Разработанный статический анализатор был протестирован на нескольких известных проектах с открытым исходным кодом, а также на решениях задач с сайта Codeforces. Проведённые эксперименты показали возможность применения продукта на практике.
[1] B. Thomas. The concept of dynamic analysis. ACM SIGSOFT Software Engineering Note, 1999, pp. 216-234.
[2] SpotBugs repository. URL: https://github.com/spotbugs/spotbugs (дата обр. 17.05.2023).
[3] Coverity. URL: https://scan. coverity. com/ (дата обр. 12.04.2023).
[4] CodeQL repository. URL: https://github.com/github/codeql/ (дата обр. 12.04.2023).
[5] FB Infer. URL: https://fbinfer.com/ (дата обр. 12.04.2023).
[6] H. Li, T. Kim, M. Bat-Erdene, H. Lee. Software Vulnerability Detection Using Backward Trace Analysis and Symbolic Execution. International Conference on Availability, Reliability and Security, 2013, pp. 446-454.
[7] CWE Top 25 Most Dangerous Software Weaknesses, 2022. URL: https://cwe.mitre.org/top25/archive/ 2022/2022_cwe_top25.html (дата обр. 12.04.2023).
[8] UnitTestBot repository. URL: https://github.com/UnitTestBot/UTBotJava/ (дата обр. 12.04.2023).
[9] Jetbrains IntelliJ IDEA repository. URL: https://github.com/JetBrains/ intellij-community (дата обр. 12.04.2023).
[10] Find Security Bugs repository. URL: https://github.com/find-sec- bugs/find-sec-bugs (дата обр. 17.05.2023).
[11] S. Afrose, Y. Xiao, S. Rahaman, B. P. Miller, D. Yao. Evaluation of Static Vulnerability Detection Tools With Java Cryptographic API Benchmarks. IEEE Transactions on Software Engineering, 2023, vol. 49, no. 2, pp. 485497.
[12] Mars Rover Curiosity’s ’Space Doctors’ On Bug Hunting In Space. URL: https://www.huffingtonpost.co.uk/2012/09/27/curiositys-doctors-mars-rover-coverity_n_1919115.html (дата обр. 19.04.2023).
[13] N. Imtiaz, L. Williams. A synopsis of static analysis alerts on open source software. HotSoS ’19: Proceedings of the 6th Annual Symposium on Hot Topics in the Science of Security, 2019, pp. 1-3.
[14] S. Afrose, S. Rahaman, D. Yao. CryptoAPI-Bench: A Comprehensive Benchmark on Java Cryptographic API Misuses. IEEE Cybersecurity Development (SecDev), 2019, pp. 49-61.
[15] S. Li, Z. Su. Finding Unstable Code via Compiler-Driven Differential Testing. ASPLOS 2023: Proceedings of the 28th ACM International Conference on Architectural Support for Programming Languages and Operating Systems, Vol. 3, 2023, pp.238-251
...