From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 1869C9B0E00; Tue, 19 Mar 2024 19:45:52 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 1869C9B0E00 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1710866752; bh=8T1K4UFZjQWIWPAPYG0/KYLxnDSvh++VZ9jb/ijVrYQ=; h=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=Fx8UB0PZSuqGo4pzdHiX5CMLgDXP7aTPLHXNVRyRtOwAAIlKPTSjIZMQcrQltfbwd ph6Tfz54iBQr6/EhAUyZNWvx21KULjGuePSCEIHzHK3d3xyi8uEzzcz9RV9fxcnFF8 /jBVNCFATSQV5adNxoPpXAUQ1QAiHGaotl20NywE= Received: from smtp54.i.mail.ru (smtp54.i.mail.ru [95.163.41.89]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 102C69B0E00 for ; Tue, 19 Mar 2024 19:45:50 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 102C69B0E00 Received: by smtp54.i.mail.ru with esmtpa (envelope-from ) id 1rmcay-00000001tLP-2HVa; Tue, 19 Mar 2024 19:45:49 +0300 To: Maxim Kokryashkin , Sergey Bronnikov Date: Tue, 19 Mar 2024 19:41:48 +0300 Message-ID: <20240319164148.22506-1-skaplun@tarantool.org> X-Mailer: git-send-email 2.44.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailru-Src: smtp X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD987C0EE6E7F0A597DDF776407DD463AED7AC1984F64828CE8182A05F538085040F7120FF10A4A82709487ABAC94A94B54D3AD5EC18786209F483B0ECC649216557959C119149068A2 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7E2331B2371EFE129EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637C0EA1E34B58229798638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D87D8D6A1DEF9CF275D99CCC2791D09399D9BF1AF6B2FBE4EFCC7F00164DA146DAFE8445B8C89999728AA50765F79006374F09588CB15B21E6389733CBF5DBD5E9C8A9BA7A39EFB766F5D81C698A659EA7CC7F00164DA146DA9985D098DBDEAEC85FF72824B19451C6F6B57BC7E6449061A352F6E88A58FB86F5D81C698A659EA73AA81AA40904B5D9A18204E546F3947CD2A98E5A6551E3E576E601842F6C81A12EF20D2F80756B5FB606B96278B59C4276E601842F6C81A127C277FBC8AE2E8B3E92ED2C6DA628BEEC76A7562686271ED91E3A1F190DE8FD2E808ACE2090B5E14AD6D5ED66289B5278DA827A17800CE76631511D42670FFE2EB15956EA79C166A417C69337E82CC275ECD9A6C639B01B78DA827A17800CE72958EBC9E2F10130731C566533BA786AA5CC5B56E945C8DA X-C1DE0DAB: 0D63561A33F958A5D18C71753A25DED45002B1117B3ED696164CDF4F81FCE9EE250A03108B67251B823CB91A9FED034534781492E4B8EEAD39B22F2BA948E1C8C79554A2A72441328621D336A7BC284946AD531847A6065A535571D14F44ED41 X-C8649E89: 1C3962B70DF3F0ADE00A9FD3E00BEEDF77DD89D51EBB7742D3581295AF09D3DF87807E0823442EA2ED31085941D9CD0AF7F820E7B07EA4CF26BAFC1659CE6CBAB26C2B4109E9EFB12A90D191DEE5202993F9C4A385A8D5A34D6C1B221ADCA664E1E51F4E63A65834AE1D1AE5850129278EFCBD3C2C35892393FF22CE83DF45F6C226CC413062362A913E6812662D5F2A5EAB5682573093F7837F15F2B5E4A70B33F2C28C22F508233FCF178C6DD14203 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojipsw8XZ5OujiexgZ7IM+9g== X-Mailru-Sender: 583F1D7ACE8F49BD4747F3DF20327BFB49ED7FCE8FD682291E153E5C60955856C1134C0490BB7895D3EB5DC462F53DF1525762887713E5F1475755348978188EF9D3679FA3DE6E791CC59163FFD68303B0DAF586E7D11B3E67EA787935ED9F1B X-Mras: Ok Subject: [Tarantool-patches] [PATCH luajit] Prevent down-recursion for side traces. X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Kaplun via Tarantool-patches Reply-To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Mike Pall Thanks to Sergey Kaplun. (cherry picked from commit cae361187e7e1e3545353fb560c032cdace32d5f) Assume we have the root trace that uses some spill slots with the corresponding stack adjustment. Then its side trace will restore stack only at its tail. It may look like the following: | ---- TRACE 4 mcode 1247 | 55557f7df953 mov rax, [r14-0xe28] | 55557f7df95a mov rax, [rax+0x30] | 55557f7df95e sub rax, rdx | 55557f7df961 cmp rax, +0x68 | 55557f7df965 jb 0x55557f7d004c ->0 | 55557f7df96b add rsp, -0x10 | ... | 55557f6efa71 cmp dword [rdx+0x4], -0x05 | 55557f6efa75 jnz 0x55557f6e004c ->0 | ... | 55557f7dfe29 add rsp, +0x10 | 55557f7dfe2d jmp 0x5555556fe573 | ---- TRACE 4 stop -> stitch | | ---- TRACE 5 start 4/0 | ---- TRACE 5 mcode 101 | 55557f6ef9d4 mov dword [0x40000518], 0x5 | ... | 55557f6efa30 add rsp, +0x10 | 55557f6efa34 jmp 0x55557f6ef9d4 | ---- TRACE 5 stop -> down-recursion Such side traces have no stack addjustment at their heads since their stack addjustment is inherited from the parent trace. The issue occurs if the side trace has a down-recursion, as mentioned above. Before any exit, we can jump back to the start of the trace several times with growing `rsp`. In that case, the `rsp` is restored incorrectly after exiting from the trace. This patch forbids down-recursion for non-root traces. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#9595 --- Branch: https://github.com/tarantool/luajit/tree/skaplun/lj-1169-down-rec-side Related issues: * https://github.com/tarantool/tarantool/issues/9595 * https://github.com/LuaJIT/LuaJIT/issues/1169 src/lj_record.c | 2 +- .../lj-1169-down-rec-side.test.lua | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 test/tarantool-tests/lj-1169-down-rec-side.test.lua diff --git a/src/lj_record.c b/src/lj_record.c index a6c48747..7fa56834 100644 --- a/src/lj_record.c +++ b/src/lj_record.c @@ -867,7 +867,7 @@ void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults) if ((pt->flags & PROTO_NOJIT)) lj_trace_err(J, LJ_TRERR_CJITOFF); if (J->framedepth == 0 && J->pt && frame == J->L->base - 1) { - if (check_downrec_unroll(J, pt)) { + if (!J->cur.root && check_downrec_unroll(J, pt)) { J->maxslot = (BCReg)(rbase + gotresults); lj_snap_purge(J); lj_record_stop(J, LJ_TRLINK_DOWNREC, J->cur.traceno); /* Down-rec. */ diff --git a/test/tarantool-tests/lj-1169-down-rec-side.test.lua b/test/tarantool-tests/lj-1169-down-rec-side.test.lua new file mode 100644 index 00000000..63f9925f --- /dev/null +++ b/test/tarantool-tests/lj-1169-down-rec-side.test.lua @@ -0,0 +1,89 @@ +local tap = require('tap') + +-- Test file to demonstrate the LuaJIT misbehaviour when recording +-- and executing a side trace with down-recursion. +-- See also: https://github.com/LuaJIT/LuaJIT/issues/1169. + +local test = tap.test('lj-1169-down-rec-side'):skipcond({ + ['Test requires JIT enabled'] = not jit.status(), +}) + +local ffi = require('ffi') + +-- XXX: simplify `jit.dump()` output. +local modf = math.modf +local ffi_new = ffi.new + +local int64_t = ffi.typeof('int64_t') + +test:plan(1) + +-- If a parent trace has more than the default amount of spill +-- slots, the `rsp` register is adjusted at the start of the trace +-- and restored after. If there is a side trace created, it +-- modifies the stack only at exit (since adjustment is inherited +-- from a parent trace). If the side trace has down-recursion (for +-- now only down-recursion to itself is used), `rsp` may be +-- modified several times before exit, so the host stack becomes +-- corrupted. +-- +-- This test provides the example of a side trace (5) with +-- down-recursion. + +local function trace_ret(arg) -- TRACE 1 start. + return arg -- TRACE 4 start 1/0; TRACE 5 start 4/0. +end + +local function extra_frame() + -- Stitch the trace (4) to prevent early down-recursion. + modf(1) + -- Start the side trace (5) with a down-recursion. + return trace_ret({}) +end + +local call_counter = 0 +local function recursive_f() -- TRACE 2 start. + -- XXX: 4 calls are needed to record the necessary traces after + -- return. With the 5th call, the traces are executed. + if call_counter > 4 then return end -- TRACE 3 start 2/1. + call_counter = call_counter + 1 + + -- Compile the root trace first. + trace_ret(1) + trace_ret(1) + + recursive_f() + -- Stop the side trace (3) here after exiting the trace (2) with + -- up-recursion. + modf(1) + + -- Start the side trace (4). + trace_ret('') + -- luacheck: no unused + -- Generate register pressure to force spills. + -- The amount is well-suited for arm64 and x86_64. + local l1 = ffi_new(int64_t, call_counter + 1) + local l2 = ffi_new(int64_t, call_counter + 2) + local l3 = ffi_new(int64_t, call_counter + 3) + local l4 = ffi_new(int64_t, call_counter + 4) + local l5 = ffi_new(int64_t, call_counter + 5) + local l6 = ffi_new(int64_t, call_counter + 6) + local l7 = ffi_new(int64_t, call_counter + 7) + local l8 = ffi_new(int64_t, call_counter + 8) + local l9 = ffi_new(int64_t, call_counter + 9) + local l10 = ffi_new(int64_t, call_counter + 10) + local l11 = ffi_new(int64_t, call_counter + 11) + local l12 = ffi_new(int64_t, call_counter + 12) + local l13 = ffi_new(int64_t, call_counter + 13) + -- Simulate the return to the same function using the additional + -- frame for down-recursion. + return trace_ret(extra_frame()) +end + +jit.opt.start('hotloop=1', 'hotexit=1', 'recunroll=1') + +recursive_f() + +test:ok(true, 'no crash during execution of a trace with down-recursion') + +test:done(true) -- 2.44.0