Skip to content

luzer: fix memory leak in FDP#66

Open
ligurio wants to merge 1 commit intomasterfrom
ligurio/gh-52-fix-fdp-memleak
Open

luzer: fix memory leak in FDP#66
ligurio wants to merge 1 commit intomasterfrom
ligurio/gh-52-fix-fdp-memleak

Conversation

@ligurio
Copy link
Copy Markdown
Owner

@ligurio ligurio commented Oct 14, 2025

Fixes #52
Fixes #65
Fixes #85

@ligurio ligurio force-pushed the ligurio/gh-52-fix-fdp-memleak branch from 66c1af6 to ff80f13 Compare October 15, 2025 13:25
@ligurio ligurio force-pushed the ligurio/gh-52-fix-fdp-memleak branch 5 times, most recently from 3b09aa9 to 9a6f771 Compare November 10, 2025 08:31
@ligurio ligurio requested a review from Buristan November 10, 2025 08:33
Copy link
Copy Markdown
Collaborator

@Buristan Buristan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sergey,
Thanks for the patch!
I suppose the next commit may be squashed into this one as well.

Please consider my comment below.

Comment thread luzer/luzer.c Outdated
LUA_SETHOOK(L, debug_hook, 0, 0);

/* Prevents memory leaks on module exit. */
lua_gc(L, LUA_GCCOLLECT, 0);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we have several smoking guns here:

  • Since Lua (and therefore LuaJIT) has the GC-based memory management model, it may accumulate memory between iterations of state mutations in the TestOneInput. Libfuzzer checks the possible leaks during fuzzing between state mutations. Hence, we should add -detect_leaks=0 (see options) to the fuzzer options to be sure that we don't fail valid runs due to these false positives. This option disables only intermediate checks. The shutdown check is still enabled (but it may be ignored if you specify the environment variable LSAN_OPTIONS). Adding of lua_gc(), may not be so helpful if the object is allocated once at the first call (loading library via require) and cleared on the exit.
  • Even with the aforementioned option the lua_close() isn't called since the process is finished by the atexit handler, which is set in the FuzzerDriver(). So, we will observe leaks of the FDP object allocated via new.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#leak-detection

Leak detection
Python is known to leak certain data, such as at interpreter initialization time. You should disable leak detection, for example with -detect_leaks=0.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Lua (and therefore LuaJIT) has the GC-based memory management model,

Honestly, I don't like the proposed solution (always disable -detect_leaks=0 for everyone). Sooner or later, this option will lead to an OOM, and we'll have to figure out why the memory leak occurred. I prefer the scenario where the test crashes at the first sign of symptoms.

I propose another solution - leave the patch as is and add a section to a documentation (done).

Even with the aforementioned option

atexit() handler helps for another case - LLVMFuzzerRunDriver() returns due to error

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I don't like the proposed solution (always disable -detect_leaks=0 for everyone). Sooner or later, this option will lead to an OOM, and we'll have to figure out why the memory leak occurred. I prefer the scenario where the test crashes at the first sign of symptoms.

The generated input (a too large string, for example) may also lead to OOM in tests as well. This is not the part of our concern. OTOH, false-positive leaks, which are possible for user code as well, remain a problem. I'll repeat it again: -detect_leaks=0 doesn't fully disable leak detection but instead postpones it until the end of the fuzzing process, so all true leaks will be reported to the user.

Without this flag, we just see more false-positives, and have no protection against OOM in generated input as well. These reports are meaningless for the GC-based VMs.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test with memory leaking:

commit d2d57befc6cc484d1e78a98a64f1b04f71a71e6d
Author: Sergey Bronnikov <sergeyb@tarantool.org>
Date:   Mon Apr 6 18:57:48 2026 +0300

    leak memory test [TO SQUASH]

diff --git a/luzer/luzer.c b/luzer/luzer.c
index f309d76..bdafab4 100644
--- a/luzer/luzer.c
+++ b/luzer/luzer.c
@@ -355,7 +355,7 @@ TestOneInput(const uint8_t* data, size_t size) {
 	LUA_SETHOOK(L, debug_hook, 0, 0);
 
 	/* Prevents memory leaks on module exit. */
-	lua_gc(L, LUA_GCCOLLECT, 0);
+	/* lua_gc(L, LUA_GCCOLLECT, 0); */
 
 	return rc;
 }
diff --git a/luzer/tests/CMakeLists.txt b/luzer/tests/CMakeLists.txt
index 284b352..3c0a80d 100644
--- a/luzer/tests/CMakeLists.txt
+++ b/luzer/tests/CMakeLists.txt
@@ -332,3 +332,17 @@ set_tests_properties(luzer_missed_dict_test PROPERTIES
   PASS_REGULAR_EXPRESSION "ParseDictionaryFile: file does not exist or is empty"
   FAIL_REGULAR_EXPRESSION "${FAIL_REGULAR_EXPRESSION}"
 )
+
+add_test(
+  NAME luzer_leak_memory_test
+  COMMAND ${LUA_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/leak_memory.lua
+          -rss_limit_mb=2048
+)
+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..3a84b0b
--- /dev/null
+++ b/luzer/tests/leak_memory.lua
@@ -0,0 +1,22 @@
+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),
+        make_big_object(size - 1)
+    }
+end
+
+local function TestOneInput(_buf)
+    local new_object = make_big_object(4)
+    table.insert(leaky_cache, new_object)
+end
+
+luzer.Fuzz(TestOneInput)
test output
UpdateCTestConfiguration  from :/home/sergeyb/sources/luzer/build/DartConfiguration.tcl
UpdateCTestConfiguration  from :/home/sergeyb/sources/luzer/build/DartConfiguration.tcl
Test project /home/sergeyb/sources/luzer/build
Constructing a list of tests
Done constructing a list of tests
Updating test list for fixtures
Added 0 tests to meet fixture requirements
Checking test dependency graph...
Checking test dependency graph end
test 25
    Start 25: luzer_leak_memory_test

25: Test command: /bin/lua5.4 "/home/sergeyb/sources/luzer/luzer/tests/leak_memory.lua"
25: Working Directory: /home/sergeyb/sources/luzer/build/luzer/tests
25: Environment variables: 
25:  LUA_CPATH=/home/sergeyb/sources/luzer/build/luzer/?.so;;
25:  LUA_PATH=/home/sergeyb/sources/luzer/?/?.lua;/home/sergeyb/sources/luzer/?/init.lua;;
25:  LD_PRELOAD=/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so
25: Test timeout computed to be: 10000000
25: INFO: Running with entropic power schedule (0xFF, 100).
25: INFO: Seed: 1771349115
25: INFO: Loaded 1 modules   (96 inline 8-bit counters): 96 [0x7fb07128d933, 0x7fb07128d993), 
25: INFO: Loaded 1 PC tables (96 PCs): 96 [0x7fb07128d998,0x7fb07128df98), 
25: INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
25: INFO: A corpus is not provided, starting from an empty corpus
25: #2	INITED exec/s: 0 rss: 35Mb
25: WARNING: no interesting inputs were found so far. Is the code instrumented for coverage?
25: This may also happen if the target rejected all inputs we tried so far
25: INFO: libFuzzer disabled leak detection after every mutation.
25:       Most likely the target function accumulates allocated
25:       memory in a global state w/o actually leaking it.
25:       You may try running this binary with -trace_malloc=[12]      to get a trace of mallocs and frees.
25:       If LeakSanitizer is enabled in this process it will still
25:       run on the process shutdown.
25: #2048	pulse  corp: 1/1b lim: 21 exec/s: 186 rss: 46Mb
25: #4096	pulse  corp: 1/1b lim: 43 exec/s: 372 rss: 55Mb
25: #8192	pulse  corp: 1/1b lim: 80 exec/s: 744 rss: 74Mb
25: #16384	pulse  corp: 1/1b lim: 163 exec/s: 1365 rss: 114Mb
25: #32768	pulse  corp: 1/1b lim: 325 exec/s: 2520 rss: 194Mb
25: #65536	pulse  corp: 1/1b lim: 652 exec/s: 4681 rss: 353Mb
25: #131072	pulse  corp: 1/1b lim: 1300 exec/s: 7710 rss: 671Mb
25: #262144	pulse  corp: 1/1b lim: 2600 exec/s: 10485 rss: 1132Mb
25: ==1326388== ERROR: libFuzzer: out-of-memory (used: 2105Mb; limit: 2048Mb)
25:    To change the out-of-memory limit use -rss_limit_mb=<N>
25: 
25: Live Heap Allocations: 1162257012 bytes in 23362842 chunks; quarantined: 2165239 bytes in 125375 chunks; 3492052 other chunks; total chunks: 26980269; showing top 95% (at most 8 unique contexts)
25: 875782264 byte(s) (75%) in 15638969 allocation(s)
25:     #0 0x7fb07151c2f0 in realloc (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x11c2f0) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #1 0x556963372f93  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lmem.c:206:22
25:     #2 0x556963372f93  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lgc.c:260:13
25:     #3 0x556963372f93  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lgc.c:271:10
25:     #4 0x556963372f93  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ltable.c:627:17
25:     #5 0x5569633775c6  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lvm.c:1375:13
25:     #6 0x556963369e7d  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:637:5
25:     #7 0x556963369e7d  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:655:3
25:     #8 0x556963369e7d in lua_callk /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1020:5
25:     #9 0x7fb0712439c1 in luaL_test_one_input /home/sergeyb/sources/luzer/luzer/luzer.c:288:2
25:     #10 0x7fb071243901 in TestOneInput /home/sergeyb/sources/luzer/luzer/luzer.c:351:11
25:     #11 0x7fb07145da99 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5da99) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #12 0x7fb07145d0a9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5d0a9) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #13 0x7fb07145ea59 in fuzzer::Fuzzer::MutateAndTestOne() (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5ea59) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #14 0x7fb07145f5a5 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5f5a5) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #15 0x7fb07144bbbd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x4bbbd) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #16 0x7fb071244060 in luaL_fuzz /home/sergeyb/sources/luzer/luzer/luzer.c:576:16
25:     #17 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:529:8
25:     #18 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:595:7
25:     #19 0x5569633763c2  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lvm.c:1684:22
25:     #20 0x55696336a025  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:637:5
25:     #21 0x55696336a025  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:655:3
25:     #22 0x55696336a025  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1038:3
25:     #23 0x5569633658f4  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:144:3
25:     #24 0x55696338ed95  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:953:12
25:     #25 0x556963369c5f in lua_pcallk /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1064:14
25:     #26 0x55696336b155 in docall /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:160:12
25:     #27 0x55696336d193 in handle_script /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:256:14
25:     #28 0x55696336d193  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:645:9
25:     #29 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:529:8
25:     #30 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:595:7
25:     #31 0x55696336a00c  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:635:13
25:     #32 0x55696336a00c  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:655:3
25:     #33 0x55696336a00c  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1038:3
25:     #34 0x5569633658f4  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:144:3
25:     #35 0x55696338ed95  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:953:12
25:     #36 0x556963369c5f in lua_pcallk /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1064:14
25:     #37 0x556963360dbd  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:673:12
25:     #38 0x7fb07102a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
25:     #39 0x7fb07102a28a in __libc_start_main csu/../csu/libc-start.c:360:3
25:     #40 0x556963360e44  (/usr/bin/lua5.4+0x7e44) (BuildId: a8d1e1fd145ddca53bb41f61b1f5d88c58d7496f)
25: 
25: 242151680 byte(s) (20%) in 7567240 allocation(s)
25:     #0 0x7fb07151c2f0 in realloc (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x11c2f0) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #1 0x55696336dabb  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lmem.c:180:14
25:     #2 0x5569633735f8  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ltable.c:573:14
25:     #3 0x556963378b33  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ltable.c:592:3
25:     #4 0x556963378b33  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lvm.c:1862:11
25:     #5 0x556963369e7d  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:637:5
25:     #6 0x556963369e7d  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:655:3
25:     #7 0x556963369e7d in lua_callk /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1020:5
25:     #8 0x7fb0712439c1 in luaL_test_one_input /home/sergeyb/sources/luzer/luzer/luzer.c:288:2
25:     #9 0x7fb071243901 in TestOneInput /home/sergeyb/sources/luzer/luzer/luzer.c:351:11
25:     #10 0x7fb07145da99 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5da99) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #11 0x7fb07145d0a9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5d0a9) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #12 0x7fb07145ea59 in fuzzer::Fuzzer::MutateAndTestOne() (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5ea59) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #13 0x7fb07145f5a5 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x5f5a5) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #14 0x7fb07144bbbd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/sergeyb/sources/luzer/build/luzer/libfuzzer_with_asan.so+0x4bbbd) (BuildId: b9f029382cdc621d4deed30c2fbe2551b87e3641)
25:     #15 0x7fb071244060 in luaL_fuzz /home/sergeyb/sources/luzer/luzer/luzer.c:576:16
25:     #16 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:529:8
25:     #17 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:595:7
25:     #18 0x5569633763c2  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lvm.c:1684:22
25:     #19 0x55696336a025  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:637:5
25:     #20 0x55696336a025  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:655:3
25:     #21 0x55696336a025  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1038:3
25:     #22 0x5569633658f4  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:144:3
25:     #23 0x55696338ed95  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:953:12
25:     #24 0x556963369c5f in lua_pcallk /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1064:14
25:     #25 0x55696336b155 in docall /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:160:12
25:     #26 0x55696336d193 in handle_script /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:256:14
25:     #27 0x55696336d193  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:645:9
25:     #28 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:529:8
25:     #29 0x556963369329  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:595:7
25:     #30 0x55696336a00c  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:635:13
25:     #31 0x55696336a00c  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:655:3
25:     #32 0x55696336a00c  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1038:3
25:     #33 0x5569633658f4  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:144:3
25:     #34 0x55696338ed95  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/ldo.c:953:12
25:     #35 0x556963369c5f in lua_pcallk /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lapi.c:1064:14
25:     #36 0x556963360dbd  /build/lua5.4-GCEpSw/lua5.4-5.4.6/src/lua.c:673:12
25:     #37 0x7fb07102a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
25:     #38 0x7fb07102a28a in __libc_start_main csu/../csu/libc-start.c:360:3
25:     #39 0x556963360e44  (/usr/bin/lua5.4+0x7e44) (BuildId: a8d1e1fd145ddca53bb41f61b1f5d88c58d7496f)
25: 
25: MS: 1 CopyPart-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
25: 0xa,
25: \012
25: artifact_prefix='./'; Test unit written to ./oom-adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
25: Base64: Cg==
25: SUMMARY: libFuzzer: out-of-memory
1/1 Test #25: luzer_leak_memory_test ...........   Passed   38.97 sec

The following tests passed:
	luzer_leak_memory_test

100% tests passed, 0 tests failed out of 1

Total Test time (real) =  38.97 sec

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with leak is reproduced with the test luzer_ffi_asan:

25/30 Test #25: luzer_ffi_asan ..................................***Failed  Required regular expression not found. Regex=[Done 10 runs in [0-9] second
]  0.08 sec
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2285787721
INFO: Loaded 2 modules   (116 inline 8-bit counters): 97 [0x7fec4c1ca933, 0x7fec4c1ca994), 19 [0x7fec4ba3d983, 0x7fec4ba3d996), 
INFO: Loaded 2 PC tables (116 PCs): 97 [0x7fec4c1ca998,0x7fec4c1cafa8), 19 [0x7fec4ba3d998,0x7fec4ba3dac8), 
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2	INITED cov: 24 ft: 25 corp: 1/1b exec/s: 0 rss: 42Mb

=================================================================
==2991==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 96 byte(s) in 6 object(s) allocated from:
    #0 0x7fec4c6e8612 in malloc (/home/runner/work/luzer/luzer/build/luzer/libfuzzer_with_asan.so+0xe8612) (BuildId: 44e48a78b3ea47b0ddbda7779c3ec810456995de)
    #1 0x7fec4bebb903 in operator new(unsigned long) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xbb903) (BuildId: 753c6c8608b61d4e67be8f0c890e03e0aa046b8b)
    #2 0x7fec4c182635 in luaL_fuzzed_data_provider /home/runner/work/luzer/luzer/luzer/fuzzed_data_provider.cc:304:28
    #3 0x55ddb2a035d5  (/usr/bin/luajit+0x785d5) (BuildId: e8ba96877bda295db49e40559a6a7038587195fd)

SUMMARY: AddressSanitizer: 96 byte(s) leaked in 6 allocation(s).
INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.

MS: 1 ShuffleBytes-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0xa,
\012
artifact_prefix='./'; Test unit written to ./leak-adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
Base64: Cg==

@ligurio ligurio force-pushed the ligurio/gh-52-fix-fdp-memleak branch 4 times, most recently from 81952f0 to 494bfe8 Compare March 24, 2026 09:14
@ligurio
Copy link
Copy Markdown
Owner Author

ligurio commented Mar 24, 2026

The second patch was squashed, the commit message was updated.

@ligurio ligurio force-pushed the ligurio/gh-52-fix-fdp-memleak branch from 494bfe8 to 3cf404e Compare March 24, 2026 10:30
@ligurio ligurio requested a review from Buristan March 24, 2026 10:36
@Buristan Buristan assigned ligurio and unassigned Buristan Apr 3, 2026
@ligurio ligurio force-pushed the ligurio/gh-52-fix-fdp-memleak branch 6 times, most recently from 573b918 to 91aaaca Compare April 8, 2026 11:28
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
@ligurio ligurio force-pushed the ligurio/gh-52-fix-fdp-memleak branch from 91aaaca to 7f5a2a9 Compare April 8, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Memory leak in LuaJIT Segfault on parsing wrong dictionary Memory leak in FDP

2 participants