[Tarantool-patches] [PATCH luajit v2] Fix frame traversal for __gc handler frames.
Sergey Kaplun
skaplun at tarantool.org
Fri Nov 19 19:41:57 MSK 2021
From: Mike Pall <mike>
Reported by Changochen.
(cherry picked from 53f82e6e2e858a0a62fd1a2ff47e9866693382e6)
A cframe unwinding is missed for a C protected frame during a search for
an error function to handle a runtime error. It leads to undefined
behaviour or crash, when raising a runtime error on stack with the CP
frame before an error function handler (for example, an error in __gc
handler).
This patch adds missing unwinding for CP frame.
Sergey Kaplun:
* added the description and the test for the problem
---
LuaJIT issue: https://github.com/LuaJIT/LuaJIT/issues/601
Branch: https://github.com/tarantool/luajit/tree/skaplun/gh-noticket-fix-gc-finderrfunc
Tarantool branch: https://github.com/tarantool/tarantool/tree/skaplun/gh-noticket-fix-gc-finderrfunc
Changes in v2:
* Create CP and C stack manually in LuaC
src/lj_err.c | 1 +
test/tarantool-tests/CMakeLists.txt | 1 +
.../lj-601-fix-gc-finderrfunc.test.lua | 35 ++++++++++++
.../lj-601-fix-gc-finderrfunc/CMakeLists.txt | 1 +
.../lj-601-fix-gc-finderrfunc/mixcframe.c | 55 +++++++++++++++++++
5 files changed, 93 insertions(+)
create mode 100644 test/tarantool-tests/lj-601-fix-gc-finderrfunc.test.lua
create mode 100644 test/tarantool-tests/lj-601-fix-gc-finderrfunc/CMakeLists.txt
create mode 100644 test/tarantool-tests/lj-601-fix-gc-finderrfunc/mixcframe.c
diff --git a/src/lj_err.c b/src/lj_err.c
index b6be357e..b520b3d3 100644
--- a/src/lj_err.c
+++ b/src/lj_err.c
@@ -585,6 +585,7 @@ static ptrdiff_t finderrfunc(lua_State *L)
if (cframe_canyield(cf)) return 0;
if (cframe_errfunc(cf) >= 0)
return cframe_errfunc(cf);
+ cf = cframe_prev(cf);
frame = frame_prevd(frame);
break;
case FRAME_PCALL:
diff --git a/test/tarantool-tests/CMakeLists.txt b/test/tarantool-tests/CMakeLists.txt
index a872fa5e..b21500a0 100644
--- a/test/tarantool-tests/CMakeLists.txt
+++ b/test/tarantool-tests/CMakeLists.txt
@@ -60,6 +60,7 @@ add_subdirectory(gh-4427-ffi-sandwich)
add_subdirectory(gh-6098-fix-side-exit-patching-on-arm64)
add_subdirectory(gh-6189-cur_L)
add_subdirectory(lj-49-bad-lightuserdata)
+add_subdirectory(lj-601-fix-gc-finderrfunc)
add_subdirectory(lj-flush-on-trace)
add_subdirectory(misclib-getmetrics-capi)
diff --git a/test/tarantool-tests/lj-601-fix-gc-finderrfunc.test.lua b/test/tarantool-tests/lj-601-fix-gc-finderrfunc.test.lua
new file mode 100644
index 00000000..d4c44489
--- /dev/null
+++ b/test/tarantool-tests/lj-601-fix-gc-finderrfunc.test.lua
@@ -0,0 +1,35 @@
+local tap = require('tap')
+
+local mixcframe = require('libmixcframe')
+local test = tap.test('lj-601-fix-gc-finderrfunc')
+test:plan(1)
+
+-- Test file to demonstrate LuaJIT incorrect behaviour, when
+-- throwing error in __gc finalizer.
+-- See also, https://github.com/LuaJIT/LuaJIT/issues/601.
+
+-- Stop GC for now.
+collectgarbage('stop')
+
+local a = newproxy(true)
+getmetatable(a).__gc = function()
+ -- Function to raise error via `lj_err_run()` inside __gc.
+ error('raise error in __gc')
+end
+-- luacheck: no unused
+a = nil
+
+-- We need to get the following Lua stack format when raise an
+-- error:
+-- + L->stack
+-- | ...
+-- | CP -- any C protected frame.
+-- | ...[L/LP/V]...
+-- | C -- any C frame.
+-- | ...[L/LP/V]...
+-- | CP (with inherited errfunc) -- __gc frame.
+-- V
+-- Enter in the C to call CP func. Call `lua_gc()` inside.
+test:ok(mixcframe.test_handle_err(), 'error in __gc is successfully handled')
+
+os.exit(test:check() and 0 or 1)
diff --git a/test/tarantool-tests/lj-601-fix-gc-finderrfunc/CMakeLists.txt b/test/tarantool-tests/lj-601-fix-gc-finderrfunc/CMakeLists.txt
new file mode 100644
index 00000000..3fed6105
--- /dev/null
+++ b/test/tarantool-tests/lj-601-fix-gc-finderrfunc/CMakeLists.txt
@@ -0,0 +1 @@
+BuildTestCLib(libmixcframe mixcframe.c)
diff --git a/test/tarantool-tests/lj-601-fix-gc-finderrfunc/mixcframe.c b/test/tarantool-tests/lj-601-fix-gc-finderrfunc/mixcframe.c
new file mode 100644
index 00000000..730e30ae
--- /dev/null
+++ b/test/tarantool-tests/lj-601-fix-gc-finderrfunc/mixcframe.c
@@ -0,0 +1,55 @@
+#include <lua.h>
+#include <lauxlib.h>
+#include <string.h>
+
+static void spoil_cframe(void)
+{
+ /*
+ * We need to map CFRAME_SIZE + lua_call frame size.
+ * 1 Kb is totally enough with overhead.
+ */
+ char a[1024];
+ /*
+ * XXX: Need a value >= 0 on C stack to be interpreted as
+ * its own errfunc, not inherited. Just put the big
+ * address (0x7f7f7f7f).
+ */
+ memset(a, 0x7f, sizeof(a));
+}
+
+static int cframe_func(lua_State *L)
+{
+ lua_gc(L, LUA_GCCOLLECT, 0);
+ return 0;
+}
+
+static int call_cframe_func(lua_State *L)
+{
+ lua_pushcfunction(L, cframe_func);
+ spoil_cframe();
+ lua_call(L, 0, 0);
+ return 0;
+}
+
+static int test_handle_err(lua_State *L)
+{
+ /*
+ * Not interested in the result, just want to be sure that
+ * unwinding in `finderrfunc()` works correctly.
+ */
+ lua_cpcall(L, call_cframe_func, NULL);
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+static const struct luaL_Reg mixcframe[] = {
+ {"test_handle_err", test_handle_err},
+ {NULL, NULL}
+};
+
+LUA_API int luaopen_libmixcframe(lua_State *L)
+{
+ luaL_register(L, "mixcframe", mixcframe);
+ return 1;
+}
+
--
2.31.0
More information about the Tarantool-patches
mailing list