Commit Diff


commit - fc4a32fe98f1da8b07f74a35f40b678692e7152b
commit + 253efe7357b42df840d4dc45a55f0cf4a765df34
blob - ef7cf4a9aa6384454941dd31063c85a568046ce3
blob + 0dc4fe20be52bd11a239b08f679bae64e99f86a2
--- CHANGELOG.md
+++ CHANGELOG.md
@@ -48,3 +48,4 @@ and this project adheres to [Semantic Versioning](http
 - Arguments checking in `Fuzz()` (#41).
 - A memory leak in a Lua-based implementation of `TestOneInput()`.
 - An initial buffer size in FuzzedDataProvider.
+- Memory leak in FuzzedDataProvider (#52).
blob - 35f0c1738ff5cc86ffeb13dee4f8730d693633f5
blob + 431246052f162aa44e83323c8d2ae2a22dabaf28
--- luzer/luzer.c
+++ luzer/luzer.c
@@ -345,6 +345,8 @@ TestOneInput(const uint8_t* data, size_t size) {
 	/* Disable debug hook. */
 	LUA_SETHOOK(L, debug_hook, 0, 0);
 
+	lua_gc(L, LUA_GCCOLLECT, 0);
+
 	return rc;
 }
 
@@ -438,6 +440,66 @@ free_argv(int argc, char **argv)
 }
 
 NO_SANITIZE static int
+os_exit(int exit_code) {
+	lua_State *L = luaL_newstate();
+	luaL_openlibs(L);
+	lua_getglobal(L, "os");
+	if (!lua_istable(L, -1)) {
+		fprintf(stderr, "Error: 'os' table not found.\n");
+		lua_close(L);
+		return 1;
+	}
+
+	lua_getfield(L, -1, "exit");
+	if (!lua_isfunction(L, -1)) {
+		fprintf(stderr, "Error: 'os.exit' function not found.\n");
+		lua_close(L);
+		return 1;
+	}
+
+	// Remove the 'os' table from the stack, leaving only 'os.exit'
+	lua_remove(L, -2);
+	// Optional: Push an exit code argument (e.g., 1 for error)
+	lua_pushinteger(L, exit_code);
+	lua_pushboolean(L, 1);
+	lua_call(L, 1, 0);
+
+	// This part will only be reached if os.exit somehow fails
+	// or doesn't exit the process.
+	lua_close(L);
+	return 0;
+}
+
+NO_SANITIZE static void
+shutdown_lua(void)
+{
+	lua_State *L = get_global_lua_state();
+	lua_gc(L, LUA_GCCOLLECT, 0);
+	luaL_cleanup(L);
+	lua_close(L);
+	set_global_lua_state(NULL);
+}
+
+int atexit_retcode;
+
+NO_SANITIZE void
+xxx(void) {
+	_exit(atexit_retcode);
+}
+
+NO_SANITIZE static void
+graceful_exit(int retcode, bool prevent_crash_report) {
+	prevent_crash_report = true;
+	if (prevent_crash_report) {
+		// Disable libfuzzer's atexit.
+		atexit_retcode = retcode;
+		atexit(&xxx);
+	}
+	shutdown_lua();
+	os_exit(retcode);
+}
+
+NO_SANITIZE static int
 luaL_fuzz(lua_State *L)
 {
 	const char *str = luaL_checkstring(L, -1);
@@ -534,14 +596,9 @@ luaL_fuzz(lua_State *L)
 
 	jit_status = luajit_has_enabled_jit(L);
 	set_global_lua_state(L);
-	int rc = LLVMFuzzerRunDriver(&argc, &argv, &TestOneInput);
+	graceful_exit(LLVMFuzzerRunDriver(&argc, &argv, &TestOneInput), true);
 
-	free_argv(argc, argv);
-	luaL_cleanup(L);
-
-	lua_pushnumber(L, rc);
-
-	return 1;
+	return 0;
 }
 
 static const struct luaL_Reg Module[] = {
blob - fe734815c350ac052811554b84c94905ec236be5
blob + 3a1ae9f89c17714f98a88349ebe0ee19c079960d
--- luzer/tests/CMakeLists.txt
+++ luzer/tests/CMakeLists.txt
@@ -237,11 +237,9 @@ if (LUA_HAS_JIT)
     "${TEST_ENV};FFI_LIB_NAME=testlib.so"
     "Done 10 runs in 0 second"
   )
-  # XXX: Memory leak in FDP is expected, should be fixed in [1].
-  # 1. https://github.com/ligurio/luzer/issues/52
   generate_ffi_test(luzer_ffi_asan
     "${TEST_ENV};LD_PRELOAD=${ASAN_DSO_PATH};FFI_LIB_NAME=testlib_asan.so"
-    "LeakSanitizer: detected memory leaks"
+    "Done 10 runs in 0 second"
   )
   generate_ffi_test(luzer_ffi_ubsan
     "${TEST_ENV};LD_PRELOAD=${UBSAN_DSO_PATH};FFI_LIB_NAME=testlib_ubsan.so"