Введение 3
Постановка задачи 5
1. Обзор предметной области 6
1.1. Символьное исполнение 6
1.2. Проблема анализа внешних вызовов с использованием символьного исполнения 9
1.3. Обзор существующих решений 11
1.4. Выводы 13
2. Архитектура символьной виртуальной машины KLEE 15
2.1. LLVM 15
2.2. Основные компоненты KLEE 15
2.2.1 Препроцессор 16
2.2.2 Интерпретатор 17
2.2.3 Конструктор запросов к Z3 18
2.3. Алгоритм воспроизведения тестов 18
3. Реализация алгоритма анализа внешних вызовов 19
3.1. Основная идея алгоритма 19
3.2. Стратегии изоляции 20
3.2.1 Недетерминированные символьные модели 20
3.2.2 Детерминированные символьные модели 20
3.2.3 Пример работы реализованных стратегий изоляции 21
3.3. Реализация символьных моделей для аллокаторов 23
3.4. Архитектура итогового решения 24
4. Тестирование 26
4.1. Тестовая инфраструктура 26
4.2. Результаты 26
4.2.1 Недетерминированные символьные модели 26
4.2.2 Детерминированные символьные модели 29
4.3. Выводы 29
Заключение 31
Список литературы 32
Важнейшей характеристикой любого программного продукта является его надёжность. Именно поэтому тестирование — неотъемлемая часть разработки программного обеспечения. Автоматизация процесса тестирования позволяет значительно снизить стоимость разработки компьютерных программ [1]. Одним из распространённых методов автоматической верификации программного обеспечения является символьное исполнение [2].
Идея классического символьного исполнения состоит в выполнении программного кода таким образом, что вместо конкретных данных на вход программа получает символы, представляющие собой произвольные значения. Такой подход позволяет анализировать даже очень редкие сценарии поведения, чего трудно добиться, используя случайное тестирование.
Одной из проблем, с которой сталкивается символьное исполнение, является анализ взаимодействия программы с внешним окружением [3]. Работа с системными вызовами, сторонними библиотеками, файлами, сетью и другими внешними объектами часто приводит к побочным эффектам, которые крайне трудно исследовать в тех случаях, когда параметры взаимодействия являются символьными.
В основном современные символьные виртуальные машины решают эту задачу, создавая собственные модели для часто используемых стандартных функций и явно вызывая остальные внешние функции с конкретными аргументами. Несмотря на то, что такой подход демонстрирует хорошие результаты при тестировании небольших проектов, он имеет ряд недостатков при работе с промышленным кодом.
Во-первых, таким образом не получится анализировать пользовательские внешние функции, которые невозможно исполнить явно в момент тестирования. Такие ситуации часто возникают в процессе промышленной разработки в силу того, что над кодом программного продукта могут параллельно работать несколько команд. Так, исследование компании Microsoft [4], направленное на изучение инструментов и рабочих привычек сотрудников компании, показало, что 54% опрошенных используют модульное тестирование с целью упрощения взаимодействия между командами.
Во-вторых, конкретизация символьных аргументов перед вызовом внешней функции может приводить к потере некоторых ветвей исполнения, а следовательно, и к пропуску критических уязвимостей. Возможность анализа каждой из достижимых инструкций является важным свойством символьного исполнения, на которое опираются многие приложения [5].
В-третьих, конкретное исполнение внешних вызовов в ходе анализа программы затрудняет воспроизводимость сгенерированных тестов, так как они могут вести себя по-разному в зависимости от внешних условий. Например, если в ходе выполнения теста вызывается функция, возвращающая текущее время, то результат может зависеть от времени на машине, где проводится тестирование. Такие нестабильные тесты негативно влияют на эффективность и надёжность процесса верификации [6].
Таким образом, для внедрения техники символьного исполнения в процессы тестирования программного обеспечения в IT-компаниях необходимо преодолеть упомянутые выше трудности. Данная работа решает эту задачу путём усовершенствования символьной виртуальной машины KLEE [7], которая занимает высокие позиции в соревнованиях по автоматическому тестированию [8], [9], [10] и лежит в основе многих современных инструментов анализа кода, таких как TracerX [11], Symbiotic 8 [12], KLOVER [13], Cloud9 [14], LibKluzzer [15] и многих других.
Работа выполнена при поддержке российского исследовательского института Huawei. Кодовая база компании Huawei насчитывает гигабайты исходного кода на языках C и C++. Для упрощения тестирования такие большие проекты разделяют на модули, тестируемые в отдельности. Отсюда возникает большое количество внешних вызовов. Поэтому данная работа имеет большое значение для процессов автоматизации тестирования компании.
В данной работе были выполнены следующие задачи.
• Рассмотрены способы анализа внешних вызовов, реализованные в различных инструментах символьного исполнения. В результате проведённого обзора было принято решение реализовать автоматическую изоляцию тестируемого кода при помощи генерации символьных моделей для внешних функций. В качестве основы для реализации алгоритма была выбрана символьная виртуальная машина KLEE.
• Разработаны два типа символьных моделей: детерминированные и недетерминированные. Их использование позволяет пользователю получать более релевантные его запросу тесты.
• Разработан и реализован алгоритм автоматической генерации символьных моделей для внешних функций. Также реализован механизм воспроизведения тестов, содержащих внешние вызовы, с использованием сгенерированных символьных моделей.
• Разработаны и реализованы символьные модели для стандартных аллокаторов языка C. Также поддержано воспроизведение тестов, полученных в результате применения данных моделей. Данная функциональность позволяет анализировать редкие сценарии выполнения программы, а также детерминировано их воспроизводить.
• Проведена апробация реализованной функциональности на проекте FRRouting, которая показала эффективность модифицированного KLEE в сравнении с оригинальным в случае анализа программ, содержащих внешние вызовы. Модифицированному KLEE удалось покрыть 1700 из 4200 тестируемых строк кода, что составило 40%. В то же время оригинальный KLEE не покрыл ни строки в связи наличием в исходном коде внешних переменных.
[1] Edvardsson J. A survey on automatic test data generation // Proceedings of the 2nd Conference on Computer Science and Engineering. - 1999. - С. 21-28.
[2] Cadar C. et al. Symbolic execution for software testing in practice: preliminary assessment // Proceedings of the 33rd International Conference on Software Engineering. - 2011. - С. 1066-1071.
[3] Baldoni R. et al. A survey of symbolic execution techniques // ACM Computing Surveys (CSUR). - 2018. - Т. 51. - №. 3. - С. 1-39.
[4] Venolia G., DeLine R., LaToza T. Software development at Microsoft observed // Microsoft Research, TR. - 2005.
[5] Ma K. K. et al. Directed symbolic execution // Static Analysis: 18th International Symposium, SAS 2011, Venice, Italy, September 14-16, 2011. Proceedings 18. - Springer Berlin Heidelberg, 2011. - С. 95-111.
[6] Parry O. et al. A survey of flaky tests // ACM Transactions on Software Engineering and Methodology (TOSEM). - 2021. - Т. 31. - №. 1. - С. 1-74.
[7] Cadar C. et al. Klee: unassisted and automatic generation of high-coverage tests for complex systems programs // OSDI. - 2008. - Т. 8. - С. 209-224.
[8] Beyer D. Software testing: 5th comparative evaluation: Test-Comp 2023 // Fundamental Approaches to Software Engineering: 26th International Conference, FASE 2023, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2023, Paris, France, April 22-27, 2023, Proceedings. - Cham: Springer Nature Switzerland, 2023. - С. 309-323.
[9] Beyer D. Advances in Automatic Software Testing: Test-Comp 2022 // FASE. -2022.-С. 321-335.
[10] Beyer D. Status report on software testing: Test-Comp 2021 // Fundamental Approaches to Software Engineering: 24th International Conference, FASE 2021, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2021, Luxembourg City, Luxembourg, March 27-April 1, 2021, Proceedings 24. - Springer International Publishing, 2021. - С. 341-357.
[11] Jaffar J. et al. TracerX: Dynamic symbolic execution with interpolation (competition contribution) // Fundamental Approaches to Software Engineering. - 2020. - Т. 12076. - С. 530.
[12] Chalupa M. et al. Symbiotic 8: Beyond Symbolic Execution: (Competition Contribution) // Tools and Algorithms for the Construction and Analysis of Systems: 27th International Conference, TACAS 2021, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2021, Luxembourg City, Luxembourg, March 27-April 1, 2021, Proceedings, Part II 27. - Springer International Publishing, 2021. - С. 453-457.
[13] Li G., Ghosh I., Rajan S. P. KLOVER: A symbolic execution and automatic test generation tool for C++ programs // Computer Aided Verification: 23rd International Conference, CAV 2011, Snowbird, UT, USA, July 14-20, 2011. Proceedings 23. - Springer Berlin Heidelberg, 2011. - С. 609-615.
[14] CiorteaL. etal. Cloud9: A software testing service//ACM SIGOPS Operating Systems Review. - 2010. - Т. 43. - №. 4. - С. 5-10.
[15] Le H. M. LLVM-based Hybrid Fuzzing with LibKluzzer (Competition Contribution) // FASE. - 2020. - С. 535-539.
...