Тестирование — одна из обязательных частей разработки программного обеспечения. Целью тестирования являются нахождение ошибок и критических уязвимостей в коде, минимизация усилий и времени, затраченных на проверку надёжности, производительности программы, её соответствия стандартам, правилам и требованиям.
Важным шагом в развитии тестирования программного обеспечения является автоматизация генерации тестов, позволяющая освободить разработчиков от одной из тяжёлых и трудоёмких частей производства продукта [1]. Основная проблема автоматизации тестирования состоит в том, что количество путей исполнения в программе может быть сколь угодно большим. Следовательно, автоматический генератор тестов должен создать столько входных аргументов для тестируемой функции, сколько необходимо для покрытия максимально возможного количества путей исполнения.
Современные подходы к решению этой проблемы являются комбинацией поведенческого (black-box test generation) и структурного (white-box test generation) тестирования [2]. Традиционным примером поведенческого тестирования является генерация случайных входных данных для программы (или же «фаззинг»), а структурного — символьное исполнение. Если сравнить поведенческое и структурное тестирование, то можно отметить, что поведенческое тестирование быстрее анализирует пути исполнения, однако обладает неточностью, поскольку не покрывает редкие, труднодостижимые пути. Наоборот, структурное тестирование отличается своей точностью, но работает медленнее, так как сталкивается с проблемой «взрыва числа путей исполнения» [3]. «Взрыв путей» происходит из-за того, что условные операторы и операторы цикла экспоненциально увеличивают количество возможных путей исполнения программы. На практике большая часть путей не представляет интереса для тестирования, поскольку не добавляет нового поведения в сравнении с уже проанализированными. Проблеме «экспоненциального взрыва» уделяется большое внимание со стороны научного сообщества [4, 5, 6].
Одним из возможных подходов структурного анализа является прямое символьное исследование кода программы, а именно символьное исполнение от точки входа. Но такой способ не может осуществить направленное исследование к конкретной инструкции в коде. В дополнение к прямому можно использовать обратное символьное исполнение, позволяющее запустить исследование в направлении от целевой инструкции к точке входа [7]. И в том, и в другом случае существует проблема анализа большого числа абстрактных путей, не реализующихся в реальности. Эффективной комбинацией этих двух подходов является двунаправленное символьное исполнение [8].
Двунаправленное исполнение может помочь в решении основной проблемы символьного исполнения — проблемы «взрыва путей». При анализе вопроса «экспоненциального взрыва» можно обнаружить, что не всегда различное количество итераций цикла будет приводить к отличающимся результатам работы программы. Существуют ситуации, в которых программа сохраняет свойство некоторого состояния вне зависимости от числа итераций цикла, другими словами, поддерживает инвариант. Инвариантами можно воспользоваться для уменьшения числа путей анализа программы, чтобы отбрасывать заведомо недостижимые участки кода.
Одним из популярных и широко используемых инструментов с открытым исходным кодом, который занимается автоматической генерацией тестов на основе символьного исполнения, является KLEE [9].
Данная работа посвящена улучшению анализа кода символьной виртуальной машиной KLEE путём внедрения вывода индуктивных инвариантов при помощи стратегии двунаправленного исполнения.
В данной работе были выполнены следующие задачи:
• Проведён обзор существующих подходов и решений в области вывода индуктивных инвариантов среди инструментов автоматической генерации тестов 2LS, Angr и KLEE. В ходе обзора в качестве основы для реализации алгоритма был выбран инструмент KLEE.
• Разработан эффективный алгоритм проверки лемм на индуктивность с применением двунаправленного символьного исполнения. Описаны необходимые модификации исходного алгоритма двунаправленного исполнения для внедрения возможности проверки лемм на индуктивность.
• Полученный алгоритм реализован в виртуальной символьной машине KLEE. Описаны необходимые структуры данных и детали реализации.
• Проведено тестирование реализации на проверочных данных соревнования SVComp. Модифицированная версия KLEE в 67% случаев позволила вывести индуктивный инвариант, тем самым упростив символьной машине анализ программы. Однако в ситуациях, когда потенциальная лемма-инвариант не встречается в качестве инструкции, алгоритм не улучшает анализ кода.
[1] Edvardsson Jon. A survey on automatic test data generation // Proceedings of the 2nd Conference on Computer Science and Engineering / Citeseer. — 1999. • P. 21-28.
[2] Nidhra Srinivas, Dondeti Jagruthi. Black box and white box testing techniques- a literature review // International Journal of Embedded Systems and Applications (IJESA). — 2012. — Vol. 2, no. 2. — P. 29-50. Algorithms, 12:3, 2016.
[3] A survey of symbolic execution techniques / Roberto Baldoni, Emilio Coppa, Daniele Cono D’elia et al. // ACM Computing Surveys (CSUR). — 2018. — Vol. 51, no. 3. — P. 1-39.
[4] Kuznetsov Volodymyr, Kinder Johannes, Bucur Stefan, and Candea George. Efficient state merging in symbolic execution // Acm Sigplan Notices. — 2012. • Vol. 47, no. 6. — P. 193-204.
[5] Avgerinos Thanassis, Rebert Alexandre, Cha Sang Kil, and Brumley David. Enhancing symbolic execution with veritesting // Proceedings of the 36th International Conference on Software Engineering. — 2014. — P. 1083-1094.
[6] Xie Tao, Tillmann Nikolai, De Halleux Jonathan, and Schulte Wolfram. Fitness-guided path exploration in dynamic symbolic execution // 2009 IEEE/IFIP International Conference on Dependable Systems Networks / IEEE. • 2009. —P. 359-368.
[7] Dinges Peter, Agha Gul. Targeted test input generation using symbolic-concrete backward execution // Proceedings of the 29th ACM/IEEE international conference on Automated software engineering. — 2014. — P. 31-36.
[8] Baluda Mauro, Denaro Giovanni, and Pezze Mauro. Bidirectional symbolic analysis for effective branch testing // IEEE Transactions on Software Engineering. -- 2015. -- Vol. 42, no. 5. -- P. 403-426.
[9] KLEE Symbolic Execution Engine. URL: https://github.com/klee/klee (дата обращения 27.04.2023).
[10] Gao Jerry, Tsao H-SJ, Wu Ye. Testing and quality assurance for componentbased software. — Artech House, 2003.
[11] Miller Barton P, Fredriksen Louis, So Bryan. An empirical study of the reliability of UNIX utilities // Communications of the ACM. — 1990. — Vol. 33, no. 12.— P. 32-44.
[12] Fraser G., Arcuri A. Evolutionary Generation of Whole Test Suites //. — 08/2011. — P. 31-40. — DOI: 10.1109/QSIC.2011.19.
[13] Fraser G., Arcuri A. EvoSuite: Automatic test suite generation for objectoriented software //. — 09/2011. — P. 416-419. — DOI: 10.1145/2025113. 2025179.
[14] Klees G. [et al.]. Evaluating Fuzz Testing — 2018.
[15] Saavedra G. J. [et al.]. A Review ofMachine Learning Applications in Fuzzing • 2019.
...