Skip to content

Commit 689f6c3

Browse files
Mike PallBuristan
authored andcommitted
Fix stack allocation after on-trace stack check.
(cherry picked from commit 204cee2) It is possible that a snapshot topslot is less than the possible topslot of the Lua stack. In that case, if the Lua stack overflows in `lj_vmevent_prepare()`, the error is raised inside `lj_vm_exit_handler()`, which has no corresponding DWARF eh_frame [1], so it leads to the crash. This patch fix-ups the topslot of the snapshot on trace exit to the maximum possible one. Sergey Kaplun: * added the description and the test for the problem [1]: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html Part of tarantool/tarantool#10199 Reviewed-by: Sergey Bronnikov <[email protected]> Reviewed-by: Maxim Kokryashkin <[email protected]> Signed-off-by: Sergey Kaplun <[email protected]> (cherry picked from commit 82820a6)
1 parent 756acf4 commit 689f6c3

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/lj_trace.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,11 @@ static void trace_stop(jit_State *J)
522522
lj_assertJ(J->parent != 0 && J->cur.root != 0, "not a side trace");
523523
lj_asm_patchexit(J, traceref(J, J->parent), J->exitno, J->cur.mcode);
524524
/* Avoid compiling a side trace twice (stack resizing uses parent exit). */
525-
traceref(J, J->parent)->snap[J->exitno].count = SNAPCOUNT_DONE;
525+
{
526+
SnapShot *snap = &traceref(J, J->parent)->snap[J->exitno];
527+
snap->count = SNAPCOUNT_DONE;
528+
if (J->cur.topslot > snap->topslot) snap->topslot = J->cur.topslot;
529+
}
526530
/* Add to side trace chain in root trace. */
527531
{
528532
GCtrace *root = traceref(J, J->cur.root);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
local tap = require('tap')
2+
3+
-- Test file to demonstrate incorrect Lua stack restoration on
4+
-- exit from trace by the stack overflow.
5+
6+
local test = tap.test('fix-stack-alloc-on-trace-exit'):skipcond({
7+
['Test requires JIT enabled'] = not jit.status(),
8+
})
9+
10+
local jit_dump = require('jit.dump')
11+
12+
test:plan(2)
13+
14+
-- Before the patch, it is possible that a snapshot topslot is
15+
-- less than the possible topslot of the Lua stack. In that case,
16+
-- if the Lua stack overflows in `lj_vmevent_prepare()`, the error
17+
-- is raised inside `lj_vm_exit_handler()`, which has no
18+
-- corresponding DWARF eh_frame, so it leads to the crash.
19+
20+
-- Need for the stack growing in `lj_vmevent_prepare`.
21+
jit_dump.start('x', '/dev/null')
22+
23+
-- Create a coroutine with a fixed stack size.
24+
local coro = coroutine.create(function()
25+
jit.opt.start('hotloop=1', 'hotexit=1', 'callunroll=1')
26+
27+
-- `math.modf` recording is NYI.
28+
-- Local `math_modf` simplifies `jit.dump()` output.
29+
local math_modf = math.modf
30+
31+
local function trace(n)
32+
n = n + 1
33+
-- luacheck: ignore
34+
-- Start a side trace here.
35+
if n % 2 == 0 then end
36+
-- Stop the recording of the side trace and a main trace,
37+
-- stitching.
38+
math_modf(1, 1)
39+
-- Grow stack, avoid tail calls.
40+
local unused = trace(n)
41+
return unused
42+
end
43+
44+
local n = 0
45+
trace(n)
46+
end)
47+
48+
local result, errmsg = coroutine.resume(coro)
49+
50+
test:ok(not result, 'correct status and no crash')
51+
test:like(errmsg, 'stack overflow', 'correct error message')
52+
53+
test:done(true)

0 commit comments

Comments
 (0)