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 72CE216E542A; Wed, 1 Apr 2026 15:39:53 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 72CE216E542A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1775047193; bh=ueCx0YD26jMA/qYmKCSx+G48m2tf26YyXrpDb5QOLHk=; h=Date:To:Cc:References:In-Reply-To:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=t9Du3EO/O0TKaB7LcMsrnjXPS32xXZ/9ca6hdFnPnkvc+TiJeNR1T+K3+DUMdUen4 ClV8yYqFSJKau4z1EON32iCVknohwpEuQGLz3d3WYNUiBkLfpt/DufXZGE575pTwkY CYdWmWq2xKpikUJFpZyFBNsFCiBn+Jri87pudQWw= Received: from send103.i.mail.ru (send103.i.mail.ru [89.221.237.198]) (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 EAB3716E542B for ; Wed, 1 Apr 2026 15:39:51 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org EAB3716E542B Received: by exim-smtp-68896ccc7f-572lm with esmtpa (envelope-from ) id 1w7urO-00000000Jga-2z8N; Wed, 01 Apr 2026 15:39:51 +0300 Content-Type: multipart/alternative; boundary="------------oEeCyVj00EAYUOI0QUAF4bqC" Message-ID: <1c5c5b72-369a-4bcd-94b4-fd8f691f2b2d@tarantool.org> Date: Wed, 1 Apr 2026 15:39:49 +0300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org References: <20260331075403.7170-1-skaplun@tarantool.org> In-Reply-To: <20260331075403.7170-1-skaplun@tarantool.org> X-Mailru-Src: smtp X-4EC0790: 10 X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD93432C92265B16DE3C074CEADFC71870D3D8FA3D6458ECA28182A05F5380850403474E3863C80F69C3DE06ABAFEAF6705CF621BE0EBEC406BADC6EEBDEAE1D079FB1AA341019AC2B8 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE78EB5DF72B5A7B6ADEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637AC83A81C8FD4AD23D82A6BABE6F325AC2E85FA5F3EDFCBAA7353EFBB55337566B07F865632A725846F72BD625C6FA0191751CF50D44FCE24D4C472F914F1315D389733CBF5DBD5E913377AFFFEAFD269176DF2183F8FC7C0A3E989B1926288338941B15DA834481FCF19DD082D7633A0EF3E4896CB9E6436389733CBF5DBD5E9D5E8D9A59859A8B652D31B9D28593E51CC7F00164DA146DA6F5DAA56C3B73B237318B6A418E8EAB86D1867E19FE14079C09775C1D3CA48CF3D321E7403792E342EB15956EA79C166A417C69337E82CC275ECD9A6C639B01B78DA827A17800CE74F0F518E68DBD4F843847C11F186F3C59DAA53EE0834AAEE X-C1DE0DAB: 0D63561A33F958A5869B9F548FC77C465002B1117B3ED6963991EAD3FDCDB36F69995D676B7B4CBE823CB91A9FED034534781492E4B8EEAD17AEC49845D0B908 X-C8649E89: 1C3962B70DF3F0AD73CAD6646DEDE191716CD42B3DD1D34CAB70F9BE574AE9C625B6776AC983F447FC0B9F89525902EE6F57B2FD27647F25E66C117BDB76D659A5C4E21FB854FD91920E3B93F122C5AF58DC0921302CDC86A5B96187BAD397EA7F638485A427D27AB8341EE9D5BE9A0A0BACF6B189A4C6D64B4FB2A6249AE8CDB993CEC18209624E8CD93680B12512CF4C41F94D744909CE2512F26BEC029E55448553D2254B8D95CD72808BE417F3B9E0E7457915DAA85F X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu53w8ahmwBjZKM/YPHZyZHvz5uv+WouB9+ObcCpyrx6l7KImUglyhkEat/+ysWwi0gdhEs0JGjl6ggRWTy1haxBpVdbIX1nthFXMZebaIdHP2ghjoIc/363UZI6Kf1ptIMVajF1spCY9aaWuQgg+uYiMg= X-Mailru-Sender: 689FA8AB762F7393520AF17B8A65FDE20AD143EF5FCCC2F7C0DFC52F8FE15B719EDCABEDC663E147EF86D5F70DA33880E41E8EF7A07863ECB274557F927329BE2DDF8182D28ACDB545BD1C3CC395C826B4A721A3011E896F X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH luajit] Avoid recording interference due to invocation of VM hooks. 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 Bronnikov via Tarantool-patches Reply-To: Sergey Bronnikov Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" This is a multi-part message in MIME format. --------------oEeCyVj00EAYUOI0QUAF4bqC Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Hi, Sergey, thanks for the patch! LGTM Sergey On 3/31/26 10:54, Sergey Kaplun wrote: > From: Mike Pall > > Thanks to Sergey Kaplun. > > (cherry picked from commit ab834de8b6a963a83046a72b5a7751dcd6cdcff0) > > If the VM event contains a trace, it may cause several inconsistencies > during the recording of another trace: > > - If there is an exit from the trace in the VM event for the 'trace > start' VM event, the JIT engine converts the newly recorded trace to > the "side trace". So, when this side exit is taken, the JIT returns > from the VM event in the middle of another frame. > > - Stitching semantics are broken for the VM events due to an > inconsistent frame link chain. > > This patch fixes these issues by forbidding stitching in the VM event > and saving the context of the JIT engine at the VM event for trace > start. > > Sergey Kaplun: > * added the description and the test for the problem > > Part of tarantool/tarantool#12134 > --- > > Branch:https://github.com/tarantool/luajit/tree/skaplun/lj-1429-1434-recording-interference > Related issues: > *https://github.com/LuaJIT/LuaJIT/issues/1429 > *https://github.com/LuaJIT/LuaJIT/issues/1434 > *https://github.com/tarantool/tarantool/issues/12134 > > src/lj_dispatch.c | 22 +++++----- > src/lj_trace.c | 11 ++++- > .../lj-1429-stitching-to-vm-event.test.lua | 35 ++++++++++++++++ > .../lj-1434-trace-start-interference.test.lua | 40 +++++++++++++++++++ > 4 files changed, 97 insertions(+), 11 deletions(-) > create mode 100644 test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua > create mode 100644 test/tarantool-tests/lj-1434-trace-start-interference.test.lua > > diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c > index 431cb3c2..e5f876b1 100644 > --- a/src/lj_dispatch.c > +++ b/src/lj_dispatch.c > @@ -523,16 +523,18 @@ out: > /* Stitch a new trace. */ > void LJ_FASTCALL lj_dispatch_stitch(jit_State *J, const BCIns *pc) > { > - ERRNO_SAVE > - lua_State *L = J->L; > - void *cf = cframe_raw(L->cframe); > - const BCIns *oldpc = cframe_pc(cf); > - setcframe_pc(cf, pc); > - /* Before dispatch, have to bias PC by 1. */ > - L->top = L->base + cur_topslot(curr_proto(L), pc+1, cframe_multres_n(cf)); > - lj_trace_stitch(J, pc-1); /* Point to the CALL instruction. */ > - setcframe_pc(cf, oldpc); > - ERRNO_RESTORE > + if (!(J2G(J)->hookmask & HOOK_VMEVENT)) { > + ERRNO_SAVE > + lua_State *L = J->L; > + void *cf = cframe_raw(L->cframe); > + const BCIns *oldpc = cframe_pc(cf); > + setcframe_pc(cf, pc); > + /* Before dispatch, have to bias PC by 1. */ > + L->top = L->base + cur_topslot(curr_proto(L), pc+1, cframe_multres_n(cf)); > + lj_trace_stitch(J, pc-1); /* Point to the CALL instruction. */ > + setcframe_pc(cf, oldpc); > + ERRNO_RESTORE > + } > } > #endif > > diff --git a/src/lj_trace.c b/src/lj_trace.c > index 0dfbfa9f..7ed4c588 100644 > --- a/src/lj_trace.c > +++ b/src/lj_trace.c > @@ -459,7 +459,11 @@ static void trace_start(jit_State *J) > J->ktrace = 0; > setgcref(J->cur.startpt, obj2gco(J->pt)); > > - lj_vmevent_send(J2G(J), TRACE, > + lj_vmevent_send_(J2G(J), TRACE, > + TValue savetv = J2G(J)->tmptv; > + TValue savetv2 = J2G(J)->tmptv2; > + TraceNo parent = J->parent; > + ExitNo exitno = J->exitno; > setstrV(V, V->top++, lj_str_newlit(V, "start")); > setintV(V->top++, traceno); > setfuncV(V, V->top++, J->fn); > @@ -474,6 +478,11 @@ static void trace_start(jit_State *J) > setintV(V->top++, -1); > } > } > + , > + J2G(J)->tmptv = savetv; > + J2G(J)->tmptv2 = savetv2; > + J->parent = parent; > + J->exitno = exitno; > ); > lj_record_setup(J); > } > diff --git a/test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua b/test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua > new file mode 100644 > index 00000000..9ad6922e > --- /dev/null > +++ b/test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua > @@ -0,0 +1,35 @@ > +local tap = require('tap') > + > +-- The test file to demonstrate the incorrect recording of the > +-- trace when stitching in the VM event. > +-- See alsohttps://github.com/LuaJIT/LuaJIT/issues/1429. > + > +local test = tap.test('lj-1429-stitching-to-vm-event'):skipcond({ > + ['Test requires JIT enabled'] = not jit.status(), > +}) > + > +test:plan(1) > + > +local function always_number(val) > + return tonumber(val) or 1 > +end > + > +-- This handler leads to stitching in the VM event. > +local function hdl() > + always_number('') > +end > + > +jit.opt.start('hotloop=1', 'hotexit=1') > + > +jit.attach(hdl, 'trace') > + > +coroutine.wrap(function() > + always_number('') > + always_number('') > + always_number(0) -- Start side trace, invoke handler. > + -- This breaks the recording semantics before the patch. > +end)() > + > +test:ok(true, 'no assertion failure') > + > +test:done(true) > diff --git a/test/tarantool-tests/lj-1434-trace-start-interference.test.lua b/test/tarantool-tests/lj-1434-trace-start-interference.test.lua > new file mode 100644 > index 00000000..c4dfbfdc > --- /dev/null > +++ b/test/tarantool-tests/lj-1434-trace-start-interference.test.lua > @@ -0,0 +1,40 @@ > +local tap = require('tap') > + > +-- The test file to demonstrate the incorrect recording of the > +-- trace when facing the trace exit in the VM event (start). > +-- See alsohttps://github.com/LuaJIT/LuaJIT/issues/1434. > + > +local test = tap.test('lj-1434-trace-start-interference'):skipcond({ > + ['Test requires JIT enabled'] = not jit.status(), > +}) > + > +test:plan(1) > + > +local function call(self) > + return self > +end > + > +local function cb() > + -- Side exit for trace 1. > + call(nil) > +end > + > +jit.opt.start('hotloop=1', 'hotexit=1'); > + > +jit.attach(cb, 'trace') > + > +coroutine.wrap(function() > + for i = 1, 4 do > + -- Record trace 1. > + call(call(i)) > + -- Start trace 2. Side exit from trace 1 in the 'trace start' > + -- VM event converts the second trace to the "side trace". > + -- After that the VM assertion `lj_assert_bad_for_arg_type()` > + -- fails, since we return from the VM event in the middle of > + -- another frame. > + end > +end)() > + > +test:ok(true, 'no assertion failure') > + > +test:done(true) --------------oEeCyVj00EAYUOI0QUAF4bqC Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit

Hi, Sergey,

thanks for the patch! LGTM

Sergey

On 3/31/26 10:54, Sergey Kaplun wrote:
From: Mike Pall <mike>

Thanks to Sergey Kaplun.

(cherry picked from commit ab834de8b6a963a83046a72b5a7751dcd6cdcff0)

If the VM event contains a trace, it may cause several inconsistencies
during the recording of another trace:

- If there is an exit from the trace in the VM event for the 'trace
  start' VM event, the JIT engine converts the newly recorded trace to
  the "side trace". So, when this side exit is taken, the JIT returns
  from the VM event in the middle of another frame.

- Stitching semantics are broken for the VM events due to an
  inconsistent frame link chain.

This patch fixes these issues by forbidding stitching in the VM event
and saving the context of the JIT engine at the VM event for trace
start.

Sergey Kaplun:
* added the description and the test for the problem

Part of tarantool/tarantool#12134
---

Branch: https://github.com/tarantool/luajit/tree/skaplun/lj-1429-1434-recording-interference
Related issues:
* https://github.com/LuaJIT/LuaJIT/issues/1429
* https://github.com/LuaJIT/LuaJIT/issues/1434
* https://github.com/tarantool/tarantool/issues/12134

 src/lj_dispatch.c                             | 22 +++++-----
 src/lj_trace.c                                | 11 ++++-
 .../lj-1429-stitching-to-vm-event.test.lua    | 35 ++++++++++++++++
 .../lj-1434-trace-start-interference.test.lua | 40 +++++++++++++++++++
 4 files changed, 97 insertions(+), 11 deletions(-)
 create mode 100644 test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua
 create mode 100644 test/tarantool-tests/lj-1434-trace-start-interference.test.lua

diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c
index 431cb3c2..e5f876b1 100644
--- a/src/lj_dispatch.c
+++ b/src/lj_dispatch.c
@@ -523,16 +523,18 @@ out:
 /* Stitch a new trace. */
 void LJ_FASTCALL lj_dispatch_stitch(jit_State *J, const BCIns *pc)
 {
-  ERRNO_SAVE
-  lua_State *L = J->L;
-  void *cf = cframe_raw(L->cframe);
-  const BCIns *oldpc = cframe_pc(cf);
-  setcframe_pc(cf, pc);
-  /* Before dispatch, have to bias PC by 1. */
-  L->top = L->base + cur_topslot(curr_proto(L), pc+1, cframe_multres_n(cf));
-  lj_trace_stitch(J, pc-1);  /* Point to the CALL instruction. */
-  setcframe_pc(cf, oldpc);
-  ERRNO_RESTORE
+  if (!(J2G(J)->hookmask & HOOK_VMEVENT)) {
+    ERRNO_SAVE
+    lua_State *L = J->L;
+    void *cf = cframe_raw(L->cframe);
+    const BCIns *oldpc = cframe_pc(cf);
+    setcframe_pc(cf, pc);
+    /* Before dispatch, have to bias PC by 1. */
+    L->top = L->base + cur_topslot(curr_proto(L), pc+1, cframe_multres_n(cf));
+    lj_trace_stitch(J, pc-1);  /* Point to the CALL instruction. */
+    setcframe_pc(cf, oldpc);
+    ERRNO_RESTORE
+  }
 }
 #endif
 
diff --git a/src/lj_trace.c b/src/lj_trace.c
index 0dfbfa9f..7ed4c588 100644
--- a/src/lj_trace.c
+++ b/src/lj_trace.c
@@ -459,7 +459,11 @@ static void trace_start(jit_State *J)
   J->ktrace = 0;
   setgcref(J->cur.startpt, obj2gco(J->pt));
 
-  lj_vmevent_send(J2G(J), TRACE,
+  lj_vmevent_send_(J2G(J), TRACE,
+    TValue savetv = J2G(J)->tmptv;
+    TValue savetv2 = J2G(J)->tmptv2;
+    TraceNo parent = J->parent;
+    ExitNo exitno = J->exitno;
     setstrV(V, V->top++, lj_str_newlit(V, "start"));
     setintV(V->top++, traceno);
     setfuncV(V, V->top++, J->fn);
@@ -474,6 +478,11 @@ static void trace_start(jit_State *J)
 	setintV(V->top++, -1);
       }
     }
+  ,
+    J2G(J)->tmptv = savetv;
+    J2G(J)->tmptv2 = savetv2;
+    J->parent = parent;
+    J->exitno = exitno;
   );
   lj_record_setup(J);
 }
diff --git a/test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua b/test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua
new file mode 100644
index 00000000..9ad6922e
--- /dev/null
+++ b/test/tarantool-tests/lj-1429-stitching-to-vm-event.test.lua
@@ -0,0 +1,35 @@
+local tap = require('tap')
+
+-- The test file to demonstrate the incorrect recording of the
+-- trace when stitching in the VM event.
+-- See also https://github.com/LuaJIT/LuaJIT/issues/1429.
+
+local test = tap.test('lj-1429-stitching-to-vm-event'):skipcond({
+  ['Test requires JIT enabled'] = not jit.status(),
+})
+
+test:plan(1)
+
+local function always_number(val)
+  return tonumber(val) or 1
+end
+
+-- This handler leads to stitching in the VM event.
+local function hdl()
+  always_number('')
+end
+
+jit.opt.start('hotloop=1', 'hotexit=1')
+
+jit.attach(hdl, 'trace')
+
+coroutine.wrap(function()
+  always_number('')
+  always_number('')
+  always_number(0) -- Start side trace, invoke handler.
+  -- This breaks the recording semantics before the patch.
+end)()
+
+test:ok(true, 'no assertion failure')
+
+test:done(true)
diff --git a/test/tarantool-tests/lj-1434-trace-start-interference.test.lua b/test/tarantool-tests/lj-1434-trace-start-interference.test.lua
new file mode 100644
index 00000000..c4dfbfdc
--- /dev/null
+++ b/test/tarantool-tests/lj-1434-trace-start-interference.test.lua
@@ -0,0 +1,40 @@
+local tap = require('tap')
+
+-- The test file to demonstrate the incorrect recording of the
+-- trace when facing the trace exit in the VM event (start).
+-- See also https://github.com/LuaJIT/LuaJIT/issues/1434.
+
+local test = tap.test('lj-1434-trace-start-interference'):skipcond({
+  ['Test requires JIT enabled'] = not jit.status(),
+})
+
+test:plan(1)
+
+local function call(self)
+  return self
+end
+
+local function cb()
+  -- Side exit for trace 1.
+  call(nil)
+end
+
+jit.opt.start('hotloop=1', 'hotexit=1');
+
+jit.attach(cb, 'trace')
+
+coroutine.wrap(function()
+  for i = 1, 4 do
+    -- Record trace 1.
+    call(call(i))
+    -- Start trace 2. Side exit from trace 1 in the 'trace start'
+    -- VM event converts the second trace to the "side trace".
+    -- After that the VM assertion `lj_assert_bad_for_arg_type()`
+    -- fails, since we return from the VM event in the middle of
+    -- another frame.
+  end
+end)()
+
+test:ok(true, 'no assertion failure')
+
+test:done(true)
--------------oEeCyVj00EAYUOI0QUAF4bqC--