International conference of developers
and users of free / open source software

Автоматизированный поиск багов в C/C++

Александр Зайцев, Минск, Belarus

LVEE 2018

Nowadays programs are too complex for verification and any developer can't guarantee that program is valid in every situation. Inthis talk I'll try to introduce modern ways for automatic testing C/C++ programs. I will talk about proper compiler settings, static and dynamic analyzers, sanitizers and fuzzing tecniques.

Сегодня хотелось бы рассказать о такой вещи как фаззинг(fuzzing) тестирование и чем оно будет полезно как для разработчиков программ, так и для их мейнтейнеров в различных дистрибутивах. Так как я в основном пользуюсь в своей работе языком программирования C++, то я буду рассказывать про фаззинг в контексте разработки на данном языке программирования (Си тоже касается :)

Почему эти языки находятся в зоне риска?

Языки программирования C/C++ заточены на производительность. За это приходится платить тем, что в ходе работы программы отсутствуют многие элементарные проверки валидности программы. Даже банальный выход за пределы массива или разыменование нулевого указателя ведёт не к исключению, а к так называемому “неопределённому поведению”, которое позволяет компилятору творить с вашей программой всё что угодно (читать как “программа становится автоматически невалидной”).

Типичные примеры неопределённого поведения:

- Разыменование нулевого указателя
- Использование невалидного указателя\итератора\ссылки
- Переполнение знаковых переменных
- Нарушение контрактов стандартных алгоритмов
- Многократное освобождение памяти
- Использование памяти после освобождения

Как будем бороться с этим?

Настройка компилятора

- Включение как можно большего числа предупреждений (аккуратнее на грязной кодовой базе, а то есть шанс утонуть)
- Обновляйте компиляторы по возможности – новые компиляторы – новые предупреждения и диагностики
- Для поддержания чистоты – pedantic, чтобы неповадно было игнорировать предупреждения

Статический анализ кода

Когда нам мало диагностик компилятора, на помощь нам могут прийти такие средства как статические анализаторы кода. В них обычно реализовано гораздо больше проверок на типичные ошибки разработчика, нежели в компиляторах (на то они и есть специализированные средства). Рекомендуемые к использованию статические анализаторы кода:
- cppcheck (GPL-3.0)
- Clang-Tidy (BSD)
- (не уверен, что стоит называть проприетарные средства тут)

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

Санитайзеры

Санитайзеры (sanitizers) – утилиты из уже поставки компиляторов, которые помогают найти проблемы в вашем коде.
Санитайзеры бывают:
- Address – помогает найти проблемы с памятью (утечка, двойное разыменование и т.д.). Работает как перехват всех операций с памятью и проверка во время работы. Значительное замедление работы
- Thread – помогает находить состояния гонки и deadlocks в вашем коде
- Undefined – помогает диагностировать неопределённое поведение в коде. Само собой разумеется, что он не может гарантированно найти все ошибки, потому что это C++.
- CFE

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

Альтернатива санитайзерам

Можно для обнаружения проблем во время исполнения программы использовать Valgrind и построенные на его базе программы. Тут нам помогут программы DRD, Helgrind, Memcheck. Если Вы пользователь Windows, то для Вас альтернативой будет являться программа Dr. Memory. Лицензия Valgrind – GPL-2.0, Dr. Memory – LGPL.

Что такое фаззинг-тестирование?

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

Рекомендуемые к использованию фаззеры:
- AFL (Apache)
- Libfuzzer (BSD)

Фаззинг и санитайзеры

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

Фаззинг для Open-Source

Для тестирования Open-Source проектов Google сделал проект Oss-Fuzz. Это проект, который автоматически запускает программы с открытым исходным кодом под фаззерами на мощностях Google. От проекта всего лишь требуется добавить свой проект в репозиторий, следовать довольно простым требованиям и дождаться одобрения от ребят из Google. И всё – потом можно расслабиться и ждать отчётов с ошибками.

В целом это всё, что я хотел рассказать. Спасибо за внимание!

Abstract licensed under Creative Commons Attribution-ShareAlike 3.0 license

Back