Фаззинг или тестирование мусорными данными

Зачем?

В качестве примера — поисковый запрос в 2ГИС, для которого используется не только сам текстовый ввод, но и параметры пользователя: предпочтения, город, текущий участок карты.

Ручное тестирование подобных вещей из-за количества комбинаций практически нереально.

Фаззинг

Фаззинг — это тестирование случайными входными данными.

Пример из реальной жизни: это Heartbleed из OpenSSL; уязвимость, которая позволяет читать память на сервере или клиенте, которая затронула гигантское количество устройств и серверов и была обнаружена только спустя два года

libFuzzer

Библиотека, которая подходит для тестирования: - парсеров - компрессии - криптографии - регулярных выражений

Нужно понимать, что он хорошо подходит для приложений на C++, т.к. имеет расширяеемое C-API для интеграции и входит в поставку с компилятором Clang. В документации, например, есть пример поиска ошибки в той самой уязвимой версии OpenSSL с помощью libFuzzer.

Фаззинг помогает найти нетривиальные баги в сложно-связанном коде.

Как это устроено?

Тестовый корпус данных -> Вызов функции -> Оценка сценариев -> Мутация данных -> 🔄 -> Артефакты бага

Однако libfuzzer работает с набором байт, а наше приложение чаще всего работает с более сложными структурированными данными, и если на него натравить libfuzzer, то мы получим множество исключений о неправильном формате, но не найдёт реальные баги.

Для того чтобы это исправить, можно воспользоваться C-API и дополнить фаззинг необходимой функциональностью, например использовать более сложные структуры и даже подсовывать тестовые данные из баз. В случае с поиском 2ГИС libFuzzer после тюнинга начал использовать реальные данные организаций, комбинировать и мутировать их, и находить реальные баги в продукте.

Что получилось у 2ГИС