In general, luzer
has an ability to write fuzzing tests for a Lua functions.
However, steps may depend on implementation of function under test. Let's
consider a three cases:
- Fuzzing a Lua function implemented in Lua
- Fuzzing a Lua function implemented in Lua C
- Fuzzing a shared library via FFI
Let's create a fuzzing test for a parser of Lua source code used in luacheck
module.
Setup a target module using luarocks
:
$ luarocks install --local luacheck
Create a file luacheck_parser_parse.lua
with fuzzing target:
local parser = require("src.luacheck.parser")
local decoder = require("luacheck.decoder")
local luzer = require("luzer")
local function TestOneInput(buf)
parser.parse(decoder.decode(buf))
end
luzer.Fuzz(TestOneInput, nil, {})
Execute test with PUC Rio Lua:
$ lua luacheck_parser_parse.lua
Lua functions could be implemented using so called Lua C API. Functions built in Lua runtime, external modules written in C/C++ are such examples. Learn more about Lua C API in chapter "24 – An Overview of the C API " of "Programming in Lua" book.
Setup module using luarocks
:
$ luarocks install --tree modules --lua-version 5.1 lua-cjson CC="clang" CFLAGS="-ggdb -fPIC -fsanitize=address" LDFLAGS="-fsanitize=address"
Installing https://luarocks.org/lua-cjson-2.1.0.6-1.src.rock
lua-cjson 2.1.0.6-1 depends on lua >= 5.1 (5.1-1 provided by VM)
clang -ggdb -fPIC -fsanitize=address -I/usr/include/lua5.1 -c lua_cjson.c -o lua_cjson.o
clang -ggdb -fPIC -fsanitize=address -I/usr/include/lua5.1 -c strbuf.c -o strbuf.o
clang -ggdb -fPIC -fsanitize=address -I/usr/include/lua5.1 -c fpconv.c -o fpconv.o
gcc -shared -o cjson.so lua_cjson.o strbuf.o fpconv.o
No existing manifest. Attempting to rebuild...
lua-cjson 2.1.0.6-1 is now installed in /home/sergeyb/sources/luzer/build/modules (license: MIT)
Setup environment and execute test:
$ export LUA_PATH="$LUA_PATH;modules/lib/lua/5.1/?.lua"
$ export LUA_CPATH="$LUA_CPATH;modules/lib/lua/5.1/?.so;./?.so"
$ mkdir cjson-corpus
$ echo -n "{}" > cjson-corpus/sample
$ luajit luzer_example_json.lua
This way could be used for fuzzing Lua runtime. Let's consider a fuzzing
target: Lear more about function loadstring()
in chapter 8 – Compilation,
Execution, and Errors of "Programming in Lua" book.
local luzer = require("luzer")
local function TestOneInput(buf)
assert(loadstring(buf)) ()
end
local args = {
max_total_time = 60,
print_final_stats = 1,
}
luzer.Fuzz(TestOneInput, nil, args)
Run fuzzing target with instrumented Lua runtime.
Lua has a FFI library that allows seamless integration with C/C++ libraries.
LuaJIT has a builtin FFI library, that allows calling
external C functions and using C data structures from pure Lua code.
FFI library allows using luzer
for fuzzing shared libraries.
Example examples/example_zlib.lua
demonstrates a test for ZLib library using
FFI. For better results it is recommended to build ZLib with sanitizers.
Run fuzzing target:
$ lua examples/example_zlib.lua
luzer
allows custom mutators to be written in Lua 5.1
(including LuaJIT), 5.2, 5.3 or 5.4.
The environment variable LIBFUZZER_LUA_SCRIPT
can be set to the path to the
Lua mutator script. The default path is ./mutator.lua
.
To run the Lua example, use
LIBFUZZER_LUA_SCRIPT=./mutator.lua example_compressed
All you need to do on the C/C++ side is adding mutator.c
or crossover.c
file as a compilation unit.
Then write a Lua script that does what you would like the fuzzer to do, you
might want to use the mutator.lua
script. The environment variable
LIBFUZZER_LUA_SCRIPT
can be set to the path to the Lua mutator
script. The default path is ./mutator.lua
. Then just run your fuzzing as
shown in the examples above.