Если вы когда-либо работали над крупномасштабным проектом, вы знаете, что поиск и отслеживание ошибок может быть очень утомительным и длительным.

Знаете ли вы, что это может быть автоматизировано? Есть несколько способов добиться этого, начиная с регулярно выполняемых модульных и интеграционных тестов для выявления регрессий, заканчивая сквозными тестами, чтобы убедиться, что функциональность работает должным образом, и многим другим.

Однако написание этих тестов также является длительным процессом, и вы упускаете некоторые трудно находимые ошибки. Мы собираемся сосредоточиться на фаззинге, автоматизированном процессе обнаружения сбоев.



Что такое фаззинг?

Согласно википедии, фаззинг это метод автоматизированного тестирования программного обеспечения, который включает предоставление неверных, неожиданных или случайных данных в качестве входных данных для компьютерной программы. (https://en.wikipedia.org/wiki/Фаззинг).

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


Фаззинг на практике

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

Здесь на помощь приходит фаззинг! Я не хотел (и не имел времени) писать тысячи тестов вручную, поэтому я просто написал базовые тесты, проверяя, что каждый хороший ввод приводит к ожидаемому результату. Чего не хватало, так это тестов типа «плохой ввод приводит к плохому выводу».


Представляем AFL++

АФЛ++ является улучшенным ответвлением AFL (American Fuzzy Lop), фаззера, первоначально разработанного Google.

Вот как это работает:
Как работает AFL++

Он очень прост в использовании, вы должны перекомпилировать свой проект, используя afl-cc и/или afl-c++, дать ему входной корпус и позволить ему работать на вас, пока вы не будете удовлетворены.


Генерация входного корпуса

Так как я хотел фаззить язык программирования, мой входной корпус было легко собрать: примеры кода, части стандартной библиотеки, несколько тестовых файлов.

Процесс выглядит следующим образом:

  1. учитывая корпус, мы хотим создать уникальный корпус для удаления входных данных из корпуса, которые не создают новый путь/покрытие в цели
  2. минимизация корпуса: чем короче входные файлы, которые по-прежнему проходят один и тот же путь внутри цели, тем лучше будет фаззинг.
# step 1)
afl-cmin -i fuzzing/corpus -o fuzzing/unique -- ${buildFolder}/arkscript @@ -L ./lib

mkdir -p fuzzing/input
cd fuzzing/unique

# step 2)
for i in *; do
  afl-tmin -i "$i" -o "../input/$i" -- ../../${buildFolder}/arkscript @@ -L ../../lib
done
Войти в полноэкранный режим

Выйти из полноэкранного режима

Возможно, вы заметили -- ${buildFolder}/arkscript @@ -L ./lib бит: это команда для запуска входных данных, с @@ имя файла ввода, сгенерированного AFL++.

Затем мы можем запустить фаззер следующим образом и получить сбои:

afl-fuzz -i fuzzing/input -o fuzzing/output -s 0 -m 64 -- ${buildFolder}/arkscript @@ -L ./lib
Войти в полноэкранный режим

Выйти из полноэкранного режима

-s 0 здесь, чтобы установить семя ГСЧ в 0, чтобы иметь возможность более легко воспроизводить сбои на основе ГСЧ; каждый сбой будет храниться под fuzzing/output/.

-m 64 устанавливает жесткое ограничение в 64 МБ ОЗУ для цели, чтобы было легче обнаруживать утечки памяти, и это предотвратит потенциальную проблему нехватки памяти для вашего компьютера. Я попытался запустить без ограничений, и, конечно же, моя система зависла через 15 минут (недостаточно памяти). Затем я играл с -m значение, 8 МБ недостаточно, и это препятствует запуску моей цели.

И вот, фаззер работает и находит для нас баги.
AFL++ работает


Анализ сбоев

Теперь самое сложное — сократить входные данные, чтобы найти наименьший входной образец, который все еще генерирует ошибку. Часто это приходится делать вручную, и это утомительный процесс, но поиск этих ошибочных входных данных вручную занял бы гораздо больше времени, так что это все равно победа!

В AFL++ есть инструменты для минимизации сбоев, которые помогут вам найти ошибки:

afl-tmin -i fuzzing/output/main/crashes/id... -o fuzzing/minimized_result -- ./build/arkscript @@ -L ./lib
Войти в полноэкранный режим

Выйти из полноэкранного режима

После того, как у вас есть наименьший возможный вклад для данного сбоя, и вы исправили его, рекомендуется сохранить его где-нибудь, чтобы иметь возможность запускать на нем следующие версии вашей программы и посмотреть, исправлена ​​ли она. . Это лично помогло мне начать сбор неверных входных данных, чтобы проверить в своих тестах, правильно ли они обрабатываются.


Несколько замечаний о фаззинге:

  1. многие сбои могут быть очень похожими, когда фаззер AFL++ находит ошибки, он использует их и выводит для поиска других ошибок.
  2. из-за 1) вы можете захотеть запустить несколько фаззеров одновременно, он найдет больше ошибок, плюс он был разработан для такой работы (один главный экземпляр и несколько вариантов)
  3. вам не нужно ограничивать память, выделенную каждому фаззеру, но если вы этого не сделаете, вы можете исчерпать всю свою оперативную память
  4. фаззер может работать очень долго и ничего не находить, это не значит, что ваша программа свободна от ошибок!

Поскольку фаззинг может потребовать много времени и ресурсов, вы можете время от времени запускать эти тесты, например, для каждого нового выпуска, а не для каждого добавленного коммита или теста.