Skip to content

Commit 43a7051

Browse files
Mike PallBuristan
authored andcommitted
Fix JIT slot overflow during up-recursion.
Reported by Sergey Kaplun. (cherry picked from commit 048972d) In the case when LuaJIT is recording the side trace after the up-recursion call, there is no check that the updated `maxslot` value doesn't overflow the `LJ_MAX_JSLOTS` limit. If it records several huge returns in a row, the overflow of the aforementioned limit may occur. This triggers an assertion failure in `rec_check_slots()`. This patch fixes it by adding the corresponding check in the `lj_record_ret()`. Sergey Kaplun: * added the description and the test for the problem Resolves tarantool/security#145 Part of tarantool/tarantool#11278 Reviewed-by: Sergey Bronnikov <[email protected]> Signed-off-by: Sergey Kaplun <[email protected]> (cherry picked from commit ad2f301)
1 parent 6fff8d8 commit 43a7051

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

src/lj_record.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,8 @@ void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults)
889889
lj_trace_err(J, LJ_TRERR_LLEAVE);
890890
} else if (J->needsnap) { /* Tailcalled to ff with side-effects. */
891891
lj_trace_err(J, LJ_TRERR_NYIRETL); /* No way to insert snapshot here. */
892-
} else if (1 + pt->framesize >= LJ_MAX_JSLOTS) {
892+
} else if (1 + pt->framesize >= LJ_MAX_JSLOTS ||
893+
J->baseslot + J->maxslot >= LJ_MAX_JSLOTS) {
893894
lj_trace_err(J, LJ_TRERR_STACKOV);
894895
} else { /* Return to lower frame. Guard for the target we return to. */
895896
TRef trpt = lj_ir_kgc(J, obj2gco(pt), IRT_PROTO);
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
local tap = require('tap')
2+
3+
-- The test file to demonstrate JIT slots overflow when compiling
4+
-- the return from the trace with up-recursion.
5+
-- See also: https://github.com/LuaJIT/LuaJIT/issues/1358.
6+
7+
local test = tap.test('lj-1358-jslot-overflow-uprecursion'):skipcond({
8+
['Test requires JIT enabled'] = not jit.status(),
9+
})
10+
11+
test:plan(1)
12+
13+
-- The test generates the functions with the following workload:
14+
--
15+
-- | local uprec_func()
16+
-- | if cond then return end
17+
-- | return 'x', --[[...]] 'x', uprec_func()
18+
-- | end
19+
-- |
20+
-- | local function empty() end
21+
-- | empty('x', --[[...]] 'x', uprec_func())
22+
--
23+
-- The recording of the return from `uprec_func()` before the call
24+
-- to `empty()` causes the assertion failure in the
25+
-- `rec_check_slots()`.
26+
27+
-- Generate a function with many return values plus up-recursion.
28+
local function generate_uprec_payload(n_returns)
29+
local str_func = [[
30+
local counter = 0
31+
local function payload_f()
32+
counter = counter + 1
33+
if counter > 5 then return end
34+
return
35+
]]
36+
for _ = 1, n_returns do
37+
str_func = str_func .. '"x", '
38+
end
39+
str_func = str_func .. [[
40+
payload_f()
41+
end
42+
return payload_f
43+
]]
44+
local f = assert(loadstring(str_func))
45+
return f()
46+
end
47+
48+
-- Generate the necessary number of locals for a huge enough
49+
-- `cbase`.
50+
local function generate_nloc_payload(n_locals)
51+
local str_func = [[
52+
-- Function to be called after return with all stack slots used.
53+
local function empty() end
54+
empty(
55+
]]
56+
for _ = 1, n_locals do
57+
str_func = str_func .. '"x", '
58+
end
59+
str_func = str_func .. [[
60+
_G.uprec_func()
61+
)
62+
]]
63+
local f = assert(loadstring(str_func))
64+
return f
65+
end
66+
67+
-- Avoid an unrelated JIT output.
68+
jit.off()
69+
-- 30 * 5 = 150 returned values for the first call.
70+
_G.uprec_func = generate_uprec_payload(30)
71+
-- Plus 100 slots for locals, plus a slot for the function to be
72+
-- called causes JIT stack slots overflow.
73+
local test_func = generate_nloc_payload(100)
74+
75+
jit.on()
76+
jit.opt.start('hotloop=1', 'hotexit=1', 'recunroll=1')
77+
78+
test_func()
79+
80+
test:ok(true, 'no assertion on JIT slots overflow')
81+
82+
test:done(true)

0 commit comments

Comments
 (0)