Фаззинг-тесты, которые мы изначально делали для нашего форка LuaJIT,
используются для непрерывного фаззинга оригинального проекта LuaJIT и
интерпретатора PUC Rio Lua. Результат двухлетней работы: 6 багов в PUC Rio Lua, превентивно найденные до релиза, и 23 бага в LuaJIT. Всё были исправлены. Использование одних и тех же тестов для разных проектов оказалось возможным благодаря тому, что PUC Rio Lua и LuaJIT предоставляют один и тот же Lua C API.
Если в этих проектах получилось успешно использовать фаззинг, то было бы здорово применить их и для другого Lua рантайма, подумал я.
Для Go есть популярный среди гоферов проект - GopherLua, это реализация Lua на Go. Для проекта написано много расширений, которые добавляют функциональность со стороны Lua. В Go есть встроенный тулинг для написания фаззинг-тестов: нужно всего лишь написать обёртку для функции, собрать специальной командой и вообщем-то всё. Но я не знаю про аналог libprotobuf-mutator в Go (гоферы, подскажете?). Поэтому сделать фаззеры для GopherLua с неструктурированными данными проще простого, а чтобы фаззинг-тест генерировал структурированные валидные данные я не смог найти решения.
Я решил попробовать изобразить из GopherLua библиотеку с Lua C API, чтобы эту библиотеку можно было скомпоновать с моими тестами и переиспользовать фаззинг для Lua применительно к GopherLua. Для интеграции Go с C есть cgo
, который предоставляет возможность использования C-библиотек в Go и экспорта Go функций в интерфейс C (генерация заголовочного файла). Если кратко, то LuaJIT C FFI мне показался удобнее, чем использование cgo
. Из того, с чем я столкнулся:
- В
cgo
нельзя указать макросы, чтобы потом эти макросы оказались в сгенерированом заголовочном файле. Поэтому часть макросов из lua.h
пришлось принести в сам тест. - в Go нельзя никак указать, что функция не принимает аргументов, чтобы в заголовочном файле у функции в параметрах был
void
. На эту тему есть тикет и вроде даже патч. - В Lua C API каждая функция первым аргументом принимает указатель на
L
, структуру, описывающую Lua стек. Я не придумал, как возвращать из Go/cgo
эту структуру, поэтому мой модуль может работать с единственной копией стека. Но для моих целей этого достаточно.
В результате этой работы можно собрать тест, который по грамматике генерирует программы на Lua и исполняет их в GopherLua. Правда не все программы исполняются одинаково успешно и иногда случаются проблемы работы с памятью (runtime error: invalid memory address or nil pointer dereference
).
#lua #fuzzing