From 969461f0df297df1a337e20e6d231f6493b635a2 Mon Sep 17 00:00:00 2001 From: Sergey Bronnikov Date: Sun, 5 Jul 2026 21:17:19 +0300 Subject: [PATCH 1/5] cmake: FORTIFY_SOURCE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: На NixOS clang wrapper через hardening flags добавляет -D_FORTIFY_SOURCE=2 после пользовательских флагов, а -O2 — до. Когда CMake переопределяет -O2 на -O0 (для ASan/UBSan тестовых целей), -D_FORTIFY_SOURCE=2 остаётся, и glibc 2.40+ выдаёт ошибку: _FORTIFY_SOURCE requires compiling with optimization (-O). Исправление (в двух файлах): 1. luzer/CMakeLists.txt:46-52 — Добавлен -U_FORTIFY_SOURCE в общие флаги компиляции (belt-and-suspenders). 2. luzer/tests/CMakeLists.txt:218-219, 350-351 — Заменены -O0 на -O1 для ASan/UBSan тестовых целей (luac_asan, luac_ubsan, testlib_asan, testlib_ubsan). -O1 — минимальный рекомендуемый уровень оптимизации для санитайзеров. --- luzer/CMakeLists.txt | 12 +++++------- luzer/tests/CMakeLists.txt | 8 ++++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/luzer/CMakeLists.txt b/luzer/CMakeLists.txt index 1db3eee..45ba562 100644 --- a/luzer/CMakeLists.txt +++ b/luzer/CMakeLists.txt @@ -43,13 +43,11 @@ add_compile_options( -Wno-unused-parameter -Wpedantic ) -if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - # It turns out that macOS set _FORTIFY_SOURCE internally, so we - # need to undefine it first, otherwise an error "'_FORTIFY_SOURCE' - # macro redefined" breaks a building. - add_compile_options(-U_FORTIFY_SOURCE) - add_compile_options(-D_FORTIFY_SOURCE=2) -endif() +# _FORTIFY_SOURCE requires optimization (-O). When building without +# optimization (Debug) or with -O0 (ASan/UBSan tests), glibc 2.40+ +# errors out. Since the toolchain may define _FORTIFY_SOURCE by +# default (e.g. NixOS), undefine it unconditionally. +add_compile_options(-U_FORTIFY_SOURCE) set(LUZER_SOURCES luzer.c compat.c diff --git a/luzer/tests/CMakeLists.txt b/luzer/tests/CMakeLists.txt index d02a1f4..6cdab3d 100644 --- a/luzer/tests/CMakeLists.txt +++ b/luzer/tests/CMakeLists.txt @@ -215,8 +215,8 @@ macro(generate_luac_lib name cflags) endmacro() generate_luac_lib(luac "") -generate_luac_lib(luac_asan "-fsanitize=address;-O0") -generate_luac_lib(luac_ubsan "-fsanitize=undefined;-O0") +generate_luac_lib(luac_asan "-fsanitize=address;-O1") +generate_luac_lib(luac_ubsan "-fsanitize=undefined;-O1") macro(generate_luac_test name env_vars pass_regex) add_test(NAME ${name} @@ -347,8 +347,8 @@ macro(generate_testlib name cflags) endmacro() generate_testlib(testlib "") -generate_testlib(testlib_asan "-fsanitize=address;-O0") -generate_testlib(testlib_ubsan "-fsanitize=undefined;-O0") +generate_testlib(testlib_asan "-fsanitize=address;-O1") +generate_testlib(testlib_ubsan "-fsanitize=undefined;-O1") macro(generate_ffi_test name env_vars pass_regex) add_test( From 516f6a9d1c9d222f70ed93db70388ce1c864163e Mon Sep 17 00:00:00 2001 From: Sergey Bronnikov Date: Fri, 17 Jul 2026 22:05:53 +0300 Subject: [PATCH 2/5] Revert "cmake: FORTIFY_SOURCE" This reverts commit 41667f6731bd18ffb4475156d153d40c93766b07. --- luzer/CMakeLists.txt | 12 +++++++----- luzer/tests/CMakeLists.txt | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/luzer/CMakeLists.txt b/luzer/CMakeLists.txt index 45ba562..1db3eee 100644 --- a/luzer/CMakeLists.txt +++ b/luzer/CMakeLists.txt @@ -43,11 +43,13 @@ add_compile_options( -Wno-unused-parameter -Wpedantic ) -# _FORTIFY_SOURCE requires optimization (-O). When building without -# optimization (Debug) or with -O0 (ASan/UBSan tests), glibc 2.40+ -# errors out. Since the toolchain may define _FORTIFY_SOURCE by -# default (e.g. NixOS), undefine it unconditionally. -add_compile_options(-U_FORTIFY_SOURCE) +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + # It turns out that macOS set _FORTIFY_SOURCE internally, so we + # need to undefine it first, otherwise an error "'_FORTIFY_SOURCE' + # macro redefined" breaks a building. + add_compile_options(-U_FORTIFY_SOURCE) + add_compile_options(-D_FORTIFY_SOURCE=2) +endif() set(LUZER_SOURCES luzer.c compat.c diff --git a/luzer/tests/CMakeLists.txt b/luzer/tests/CMakeLists.txt index 6cdab3d..d02a1f4 100644 --- a/luzer/tests/CMakeLists.txt +++ b/luzer/tests/CMakeLists.txt @@ -215,8 +215,8 @@ macro(generate_luac_lib name cflags) endmacro() generate_luac_lib(luac "") -generate_luac_lib(luac_asan "-fsanitize=address;-O1") -generate_luac_lib(luac_ubsan "-fsanitize=undefined;-O1") +generate_luac_lib(luac_asan "-fsanitize=address;-O0") +generate_luac_lib(luac_ubsan "-fsanitize=undefined;-O0") macro(generate_luac_test name env_vars pass_regex) add_test(NAME ${name} @@ -347,8 +347,8 @@ macro(generate_testlib name cflags) endmacro() generate_testlib(testlib "") -generate_testlib(testlib_asan "-fsanitize=address;-O1") -generate_testlib(testlib_ubsan "-fsanitize=undefined;-O1") +generate_testlib(testlib_asan "-fsanitize=address;-O0") +generate_testlib(testlib_ubsan "-fsanitize=undefined;-O0") macro(generate_ffi_test name env_vars pass_regex) add_test( From 30f37b2edf8b8cb9a464e58b4d7586e8b94240a5 Mon Sep 17 00:00:00 2001 From: Sergey Bronnikov Date: Fri, 17 Jul 2026 22:04:37 +0300 Subject: [PATCH 3/5] leak fdp repro --- luzer/tests/CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/luzer/tests/CMakeLists.txt b/luzer/tests/CMakeLists.txt index d02a1f4..6629268 100644 --- a/luzer/tests/CMakeLists.txt +++ b/luzer/tests/CMakeLists.txt @@ -419,3 +419,15 @@ if (LUA_HAS_JIT) "runtime error: load of null pointer of type" ) endif() + +# FuzzedDataProvider leak test (issue #52) +add_test( + NAME luzer_leak_fdp + COMMAND ${LUA_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_leak_fdp.lua + -runs=100 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +set_tests_properties(luzer_leak_fdp PROPERTIES + ENVIRONMENT "LUA_CPATH=${LUA_CPATH};LUA_PATH=${LUA_PATH};LD_PRELOAD=${ASAN_DSO_PATH}" + PASS_REGULAR_EXPRESSION "LeakSanitizer: detected memory leaks" +) From ef048e1c08f6a9814797adba2e0bba8beb3ec18e Mon Sep 17 00:00:00 2001 From: Sergey Bronnikov Date: Tue, 14 Oct 2025 21:35:43 +0300 Subject: [PATCH 4/5] luzer: fix memory leaks The patch fixes memory leaks in luzer that manifest themselves in different ways: 1. `LLVMFuzzerRunDriver()` returns due to error with dictionary. 2. Lua objects accumulate memory between `TestOneInput()` runs, but the cause is the same for everyone: Lua is a language with GC-based memory management, and after finishing the work, we did not free the memory occupied by Lua objects, so libFuzzer thinks there was a memory leak. 1. https://llvm.org/docs/LibFuzzer.html#options Fixes #52 Fixes #65 Fixes #85 --- CHANGELOG.md | 2 ++ docs/usage.md | 24 ++++++++++++++++++++ luzer/init.lua | 13 +++++++++++ luzer/luzer.c | 37 ++++++++++++++++++++++++------- luzer/tests/CMakeLists.txt | 40 ++++++++++++++++++++++++++++++---- luzer/tests/leak_memory.lua | 21 ++++++++++++++++++ luzer/tests/test_options_3.lua | 10 +++++++++ 7 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 luzer/tests/leak_memory.lua create mode 100644 luzer/tests/test_options_3.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index feba263..1bf022f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,3 +61,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - An error when Clang couldn't find the library. - A possible corruption on copying library (#91). - A crash due to incorrect use of signal handler (#40). +- Memory leak in FuzzedDataProvider (#52). +- Segfault on parsing a broken dictionary (#65). diff --git a/docs/usage.md b/docs/usage.md index f92c9e3..bc7460c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -228,9 +228,33 @@ and can be disabled by setting the enviroment variable `DISABLE_LUAJIT_METRICS`. Learn more about the enviroment variable in the section [LuaJIT Metrics](#luajit-metrics). +### Memory leaks + +When a target application (or a fuzzer) consumes increasing +amounts of RAM over time without releasing it, it can be a normal +memory consumption or memory leak is occurring. The common causes +of memory leak are: unreleased references, improper handling of +native resources in Lua. If you are encountering a memory leak in +a target application while using luzer, you may need to use memory +debugging tools to identify the specific code segment that isn't +freeing memory. + +luzer can encounter false positive memory leaks during testing. +When fuzzing native extensions, using FDP (FuzzingDataProvider), or +using FFI memory leak detection (ASan) should be disabled to +prevent false reports. Set flag `-detect_leaks=0` using enviroment +variable [`ASAN_OPTIONS`][asan-flags] as it is recommended by +AddressSanitizer: + +``` +SUMMARY: AddressSanitizer: 96 byte(s) leaked in 6 allocation(s). +INFO: to ignore leaks on libFuzzer side use -detect_leaks=0. +``` + [ffi-library-url]: https://luajit.org/ext_ffi.html [programming-in-lua-8]: https://www.lua.org/pil/8.html [programming-in-lua-24]: https://www.lua.org/pil/24.html [atheris-native-extensions]: https://github.com/google/atheris/blob/master/native_extension_fuzzing.md [atheris-native-extensions-video]: https://www.youtube.com/watch?v=oM-7lt43-GA [luacov-website]: https://lunarmodules.github.io/luacov/ +[asan-flags]: https://github.com/google/sanitizers/wiki/addresssanitizerflags diff --git a/luzer/init.lua b/luzer/init.lua index df079e3..ba43387 100644 --- a/luzer/init.lua +++ b/luzer/init.lua @@ -68,7 +68,20 @@ local function Fuzz(test_one_input, custom_mutator, func_args) if type(luzer_args) ~= "table" then error("args is not a table") end + local flags = build_flags(arg, luzer_args) + + -- Lua has the GC-based memory management model, it may + -- accumulate memory between the TestOneInput() runs. + -- libFuzzer has a flag `detect_leaks`, it is enabled by + -- default, when the option is enabled and if LeakSanitizer is + -- enabled it tries to detect memory leaks during fuzzing + -- (i.e. not only at shut down). The option `detect_leaks` may + -- lead to false positives, so it is disabled by default. + if flags["detect_leaks"] == nil then + flags["detect_leaks"] = 0 + end + local test_path = arg[0] local lua_bin = progname(arg) local test_cmd = ("%s %s"):format(lua_bin, test_path) diff --git a/luzer/luzer.c b/luzer/luzer.c index 4470713..ed00029 100644 --- a/luzer/luzer.c +++ b/luzer/luzer.c @@ -435,6 +435,33 @@ free_argv(int argc, char **argv) free(argv); } +NO_SANITIZE static void +shutdown_lua(void) +{ + lua_State *L = get_global_lua_state(); + luaL_cleanup(L); + lua_close(L); + set_global_lua_state(NULL); +} + +int atexit_retcode; + +NO_SANITIZE void +atexit_handler(void) { + _exit(atexit_retcode); +} + +NO_SANITIZE static void +graceful_exit(int retcode, bool prevent_crash_report) { + if (prevent_crash_report) { + /* Disable libFuzzer's atexit(). */ + atexit_retcode = retcode; + atexit(&atexit_handler); + } + shutdown_lua(); + exit(retcode); +} + NO_SANITIZE static int luaL_fuzz(lua_State *L) { @@ -534,14 +561,8 @@ luaL_fuzz(lua_State *L) jit_status = luajit_has_enabled_jit(L); #endif set_global_lua_state(L); - int rc = LLVMFuzzerRunDriver(&argc, &argv, &TestOneInput); - - free_argv(argc, argv); - luaL_cleanup(L); - - lua_pushnumber(L, rc); - - return 1; + graceful_exit(LLVMFuzzerRunDriver(&argc, &argv, &TestOneInput), true); + return 0; } static const struct luaL_Reg Module[] = { diff --git a/luzer/tests/CMakeLists.txt b/luzer/tests/CMakeLists.txt index 6629268..96c7b7f 100644 --- a/luzer/tests/CMakeLists.txt +++ b/luzer/tests/CMakeLists.txt @@ -363,6 +363,11 @@ macro(generate_ffi_test name env_vars pass_regex) ) endmacro() +list(APPEND LSAN_REGULAR_EXPRESSION + "LeakSanitizer: detected memory leaks" + "[0-9]+ byte(s) leaked in [0-9]+ allocation" +) + list(APPEND TEST_ENV "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR};" ) @@ -376,10 +381,7 @@ if (LUA_HAS_JIT) LD_PRELOAD=${ASAN_DSO_PATH} FFI_LIB_NAME=testlib_asan${CMAKE_SHARED_LIBRARY_SUFFIX} ) - # XXX: Memory leak in FDP is expected on Linux, should be fixed - # in [1]. - # 1. https://github.com/ligurio/luzer/issues/52 - set(PASS_PATTERN "LeakSanitizer: detected memory leaks") + set(PASS_PATTERN ${LSAN_REGULAR_EXPRESSION}) if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(PASS_PATTERN "Done 10 runs in 0 second") endif() @@ -431,3 +433,33 @@ set_tests_properties(luzer_leak_fdp PROPERTIES ENVIRONMENT "LUA_CPATH=${LUA_CPATH};LUA_PATH=${LUA_PATH};LD_PRELOAD=${ASAN_DSO_PATH}" PASS_REGULAR_EXPRESSION "LeakSanitizer: detected memory leaks" ) + +string(JOIN ";" TEST_ENV + "LUA_CPATH=${LUA_CPATH}" + "LUA_PATH=${LUA_PATH}" + "LD_PRELOAD=${ASAN_DSO_PATH}" +) +add_test( + NAME luzer_missed_dict_test + COMMAND ${LUA_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_options_3.lua + -dict=${CMAKE_CURRENT_SOURCE_DIR}/nonexistent.dict +) +set_tests_properties(luzer_missed_dict_test PROPERTIES + ENVIRONMENT "${TEST_ENV}" + PASS_REGULAR_EXPRESSION "ParseDictionaryFile: file does not exist or is empty" + FAIL_REGULAR_EXPRESSION "${LSAN_REGULAR_EXPRESSION}" +) + +add_test( + NAME luzer_leak_memory_test + COMMAND ${LUA_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/leak_memory.lua + -rss_limit_mb=500 +) +list(APPEND PASS_REGULAR_EXPRESSION + "libFuzzer disabled leak detection after every mutation" + "ERROR: libFuzzer: out-of-memory" +) +set_tests_properties(luzer_leak_memory_test PROPERTIES + ENVIRONMENT "${TEST_ENV}" + PASS_REGULAR_EXPRESSION "${PASS_REGULAR_EXPRESSION}" +) diff --git a/luzer/tests/leak_memory.lua b/luzer/tests/leak_memory.lua new file mode 100644 index 0000000..5a96989 --- /dev/null +++ b/luzer/tests/leak_memory.lua @@ -0,0 +1,21 @@ +local luzer = require("luzer") + +local leaky_cache = {} -- luacheck: no unused + +-- Function to create a large, nested table. +local function make_big_object(size) + if size <= 0 then + return {} + end + -- Create nested tables recursively. + return { + make_big_object(size - 1) + } +end + +local function TestOneInput(_buf) + local new_object = make_big_object(2) + table.insert(leaky_cache, new_object) +end + +luzer.Fuzz(TestOneInput) diff --git a/luzer/tests/test_options_3.lua b/luzer/tests/test_options_3.lua new file mode 100644 index 0000000..ecd7491 --- /dev/null +++ b/luzer/tests/test_options_3.lua @@ -0,0 +1,10 @@ +local luzer = require("luzer") + +local function TestOneInput(buf) + local fdp = luzer.FuzzedDataProvider(buf) + local str = fdp:consume_string(100) + local str_chars = {} + str:gsub(".", function(c) table.insert(str_chars, c) end) +end + +luzer.Fuzz(TestOneInput) From 5ee152b31c63473256ca12e8cb81860eb4b308ec Mon Sep 17 00:00:00 2001 From: Sergey Bronnikov Date: Wed, 8 Apr 2026 18:22:12 +0300 Subject: [PATCH 5/5] xxx --- luzer/fuzzed_data_provider.cc | 48 ++++++++++++++++ luzer/fuzzed_data_provider.h | 3 + luzer/luzer.c | 103 ++++++++++++++++++++-------------- luzer/tests/CMakeLists.txt | 2 +- luzer/tests/test_leak_fdp.lua | 9 +++ 5 files changed, 121 insertions(+), 44 deletions(-) create mode 100644 luzer/tests/test_leak_fdp.lua diff --git a/luzer/fuzzed_data_provider.cc b/luzer/fuzzed_data_provider.cc index 9ddd54a..6ce2511 100644 --- a/luzer/fuzzed_data_provider.cc +++ b/luzer/fuzzed_data_provider.cc @@ -27,6 +27,52 @@ int table_nkeys(lua_State *L, int idx); */ #define FDP_LUA_UDATA_NAME "fdp" +#define FDP_REFS_TABLE "LUZER_FDP_REFS" + +#if LUA_VERSION_NUM == 501 +#define lua_rawlen(L, idx) lua_objlen((L), (idx)) +#endif + +NO_SANITIZE int +ref_fdp(lua_State *L) { + luaL_checktype(L, -1, LUA_TUSERDATA); + lua_pushvalue(L, -1); + int ref = luaL_ref(L, LUA_REGISTRYINDEX); + + lua_getfield(L, LUA_REGISTRYINDEX, FDP_REFS_TABLE); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, FDP_REFS_TABLE); + } + int n = lua_rawlen(L, -1); + lua_pushinteger(L, ref); + lua_rawseti(L, -2, n + 1); + lua_pop(L, 1); + + return 0; +} + +NO_SANITIZE void +unref_fdp(lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, FDP_REFS_TABLE); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return; + } + int n = lua_rawlen(L, -1); + for (int i = 1; i <= n; i++) { + lua_rawgeti(L, -1, i); + int ref = lua_tointeger(L, -1); + luaL_unref(L, LUA_REGISTRYINDEX, ref); + lua_pop(L, 1); + } + lua_pushnil(L); + lua_setfield(L, LUA_REGISTRYINDEX, FDP_REFS_TABLE); + lua_pop(L, 1); +} + /* * A convenience wrapper turning the raw fuzzer input bytes into Lua primitive * types. The methods behave similarly to math.random(), with all returned @@ -307,5 +353,7 @@ luaL_fuzzed_data_provider(lua_State *L) luaL_getmetatable(L, FDP_LUA_UDATA_NAME); lua_setmetatable(L, -2); + ref_fdp(L); + return 1; } diff --git a/luzer/fuzzed_data_provider.h b/luzer/fuzzed_data_provider.h index 196137e..9fcb93a 100644 --- a/luzer/fuzzed_data_provider.h +++ b/luzer/fuzzed_data_provider.h @@ -4,6 +4,9 @@ #ifdef __cplusplus extern "C" { #endif + int ref_fdp(lua_State *L); + void unref_fdp(lua_State *L); + void fdp_metatable_init(lua_State *L); int luaL_fuzzed_data_provider(lua_State *L); #ifdef __cplusplus diff --git a/luzer/luzer.c b/luzer/luzer.c index ed00029..90f2625 100644 --- a/luzer/luzer.c +++ b/luzer/luzer.c @@ -151,9 +151,9 @@ get_symbol_path(void *addr) { return path; } -const char *dso_path_lf_asan; -const char *dso_path_lf_ubsan; -const char *dso_path_libcustom_mutator; +char *dso_path_lf_asan; +char *dso_path_lf_ubsan; +char *dso_path_libcustom_mutator; NO_SANITIZE void init(void) @@ -290,6 +290,59 @@ teardown(void) metrics_print(); } +NO_SANITIZE static int +luzer_cleanup(lua_State *L) +{ + lua_pushnil(L); + lua_setglobal(L, TEST_ONE_INPUT_FUNC); + lua_pushnil(L); + lua_setglobal(L, DEBUG_HOOK_FUNC); + lua_pushnil(L); + lua_setglobal(L, CUSTOM_MUTATOR_FUNC); + unref_fdp(L); + + free(dso_path_lf_asan); + dso_path_lf_asan = NULL; + free(dso_path_lf_ubsan); + dso_path_lf_ubsan = NULL; + free(dso_path_libcustom_mutator); + dso_path_libcustom_mutator = NULL; + + return 0; +} + +NO_SANITIZE static void +shutdown_lua(void) +{ + lua_State *L = get_global_lua_state(); + luzer_cleanup(L); + lua_settop(L, 0); + lua_gc(L, LUA_GCCOLLECT, 0); + lua_gc(L, LUA_GCCOLLECT, 0); + lua_close(L); + set_global_lua_state(NULL); +} + +int atexit_retcode; + +NO_SANITIZE void +atexit_handler(void) +{ + _exit(atexit_retcode); +} + +NO_SANITIZE static void +graceful_exit(int retcode, bool prevent_crash_report) +{ + if (prevent_crash_report) { + /* Override libFuzzer's atexit(). */ + atexit_retcode = retcode; + atexit(&atexit_handler); + } + shutdown_lua(); + exit(retcode); +} + NO_SANITIZE int TestOneInput(const uint8_t* data, size_t size) { const counter_and_pc_table_range alloc = allocate_counters_and_pcs(); @@ -346,18 +399,6 @@ TestOneInput(const uint8_t* data, size_t size) { return rc; } -NO_SANITIZE static int -luaL_cleanup(lua_State *L) -{ - lua_pushnil(L); - lua_setglobal(L, TEST_ONE_INPUT_FUNC); - lua_pushnil(L); - lua_setglobal(L, DEBUG_HOOK_FUNC); - lua_pushnil(L); - lua_setglobal(L, CUSTOM_MUTATOR_FUNC); - return 0; -} - NO_SANITIZE static int luaL_path(lua_State *L) { lua_createtable(L, 0, 3); @@ -435,33 +476,6 @@ free_argv(int argc, char **argv) free(argv); } -NO_SANITIZE static void -shutdown_lua(void) -{ - lua_State *L = get_global_lua_state(); - luaL_cleanup(L); - lua_close(L); - set_global_lua_state(NULL); -} - -int atexit_retcode; - -NO_SANITIZE void -atexit_handler(void) { - _exit(atexit_retcode); -} - -NO_SANITIZE static void -graceful_exit(int retcode, bool prevent_crash_report) { - if (prevent_crash_report) { - /* Disable libFuzzer's atexit(). */ - atexit_retcode = retcode; - atexit(&atexit_handler); - } - shutdown_lua(); - exit(retcode); -} - NO_SANITIZE static int luaL_fuzz(lua_State *L) { @@ -561,7 +575,10 @@ luaL_fuzz(lua_State *L) jit_status = luajit_has_enabled_jit(L); #endif set_global_lua_state(L); - graceful_exit(LLVMFuzzerRunDriver(&argc, &argv, &TestOneInput), true); + int rc = LLVMFuzzerRunDriver(&argc, &argv, &TestOneInput); + free_argv(argc, argv); + free((void *)corpus_path); + graceful_exit(rc, true); return 0; } diff --git a/luzer/tests/CMakeLists.txt b/luzer/tests/CMakeLists.txt index 96c7b7f..ae9514c 100644 --- a/luzer/tests/CMakeLists.txt +++ b/luzer/tests/CMakeLists.txt @@ -431,7 +431,7 @@ add_test( ) set_tests_properties(luzer_leak_fdp PROPERTIES ENVIRONMENT "LUA_CPATH=${LUA_CPATH};LUA_PATH=${LUA_PATH};LD_PRELOAD=${ASAN_DSO_PATH}" - PASS_REGULAR_EXPRESSION "LeakSanitizer: detected memory leaks" + PASS_REGULAR_EXPRESSION "Done 100 runs in [0-9]+ second" ) string(JOIN ";" TEST_ENV diff --git a/luzer/tests/test_leak_fdp.lua b/luzer/tests/test_leak_fdp.lua new file mode 100644 index 0000000..a29637c --- /dev/null +++ b/luzer/tests/test_leak_fdp.lua @@ -0,0 +1,9 @@ +local luzer = require("luzer") + +local function TestOneInput(buf) + local fdp = luzer.FuzzedDataProvider(buf) + fdp:consume_string(1) +end + +local opts = { runs = 100 } +luzer.Fuzz(TestOneInput, nil, opts)