Найти в Дзене
9 подписчиков

Фаззинг-тесты, которые мы изначально делали для нашего форка 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.

Из того, с чем я столкнулся:

- В коде Go нельзя указать макросы, чтобы потом эти макросы cgo добавил в сгенерированный заголовочной файл. Поэтому часть макросов из lua.h пришлось принести в сам тест.
- в Go нельзя никак указать, что функция не принимает аргументов, чтобы в заголовочном файле у функции в параметрах был void. На эту тему есть тикет и вроде даже патч.
- В Lua C API каждая функция первым аргументом принимает указатель на L, структуру, описывающую Lua стек. Я не придумал, как возвращать из Go/cgo эту структуру, поэтому мой модуль может работать с единственной копией стека. Но для моих целей этого достаточно.

В результате этой работы можно собрать тест, который по грамматике генерирует программы на Lua и исполняет их в GopherLua. Правда не все программы исполняются одинаково успешно и иногда случаются проблемы работы с памятью (runtime error: invalid memory address or nil pointer dereference). Такие дела.
2 минуты