From 2e82d305ebe9a21d026e41603714170fe724b3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Rodr=C3=ADguez?= Date: Thu, 5 Feb 2026 12:21:38 +0100 Subject: [PATCH] Fix computed goto into statement expression (undefined behavior) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DEF_OPC_JMP macros in interp-inlining.h place BRANCH labels inside GCC statement expressions ({ }). These labels are targets of computed gotos via the interpreter dispatch table. Per GCC documentation, jumping into a statement expression with a computed goto is undefined behavior [1], so this is fragile and not guaranteed to work. Although GCC does detect this in some simple cases, it does not catch it in JamVM, possibly due to the way the code is structured (label addresses are collected into the dispatch table separately from the goto *pc->handler dispatch in the interpreter loop). Clang 17+ performs whole-function analysis of indirect goto targets and rejects the code with a hard error: error: cannot jump from this indirect goto statement to one of its possible targets note: possible target of indirect goto statement note: jump enters a statement expression Only interp-inlining.h is affected. The other interpreter variants (interp-direct.h, interp-indirect.h) do not create labels inside statement expressions in their BRANCH macros. Fix by changing ({ }) to { } in DEF_OPC_JMP. The statement expression return value was never used (BODY is in statement context and always exits via goto *pc->handler), so the change is purely syntactic. Double checked that GCC produces byte-identical machine code with and without this change for all optimization levels. [1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html Signed-off-by: Guillermo Rodríguez --- src/interp/engine/interp-inlining.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/interp/engine/interp-inlining.h b/src/interp/engine/interp-inlining.h index 4ee5c5a..3d46563 100644 --- a/src/interp/engine/interp-inlining.h +++ b/src/interp/engine/interp-inlining.h @@ -312,23 +312,23 @@ opc##x##_##y##_##z: DEF_OPC(opcode, 0, ({BODY});) #define DEF_OPC_JMP(TYPE, BODY) \ - DEF_OPC_LBLS(OPC_##TYPE, 2, /**/, ({ \ + DEF_OPC_LBLS(OPC_##TYPE, 2, /**/, { \ *ostack++ = cache.i.v1; \ *ostack++ = cache.i.v2; \ BODY \ BRANCH(TYPE, 2, TRUE); \ - });) \ + }) \ \ - DEF_OPC_LBLS(OPC_##TYPE, 1, /**/, ({ \ + DEF_OPC_LBLS(OPC_##TYPE, 1, /**/, { \ *ostack++ = cache.i.v1; \ BODY \ BRANCH(TYPE, 1, TRUE); \ - });) \ + }) \ \ - DEF_OPC_LBLS(OPC_##TYPE, 0, /**/, ({ \ + DEF_OPC_LBLS(OPC_##TYPE, 0, /**/, { \ BODY \ BRANCH(TYPE, 0, TRUE); \ - });) + }) #define RW_LABEL(opcode, lbl) \ label(opcode, 0, lbl) \ @@ -347,10 +347,10 @@ opc##x##_##y##_##z: DEF_OPC(opcode, 0, BODY) #define DEF_OPC_JMP(TYPE, BODY) \ - DEF_OPC_LBLS(OPC_##TYPE, 0, /**/, ({ \ + DEF_OPC_LBLS(OPC_##TYPE, 0, /**/, { \ BODY \ BRANCH(TYPE, 0, TRUE); \ - });) + }) #define RW_LABEL(opcode, lbl) \ label(opcode, 0, lbl)