Автоматизированный поиск багов в C/C++
LVEE 2018
Сегодня хотелось бы рассказать о такой вещи как фаззинг(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
Назад