From 3fbb782e412a370d47618cd102071460861c3f1b Mon Sep 17 00:00:00 2001 From: RV7PR <17615589+RV7PR@users.noreply.github.com> Date: Fri, 4 Sep 2026 21:21:27 +0200 Subject: [PATCH] Fix GH-17626: JIT corrupts opline handler when blacklisting root trace When a trace exits through a ZEND_JIT_EXIT_INVALIDATE guard while ZEND_JIT_TRACE_NUM has already reached opcache.jit_max_root_traces, zend_jit_trace_exit() blacklists the root trace and restores the original VM handler. It wrote that handler to the exit opline (the INIT_* opline whose callee guard failed) instead of the root trace's start opline. The opcodes live in SHM, so every worker keeps executing the foreign handler until restart. For a root trace that starts at the entry of a function with typed parameters the copied handler is ZEND_RECV, which then runs on an INIT_FCALL / INIT_STATIC_METHOD_CALL opline and raises "Too few arguments to function X(), N passed ... and exactly N expected" from a frame that has all its arguments. Other root oplines lead to crashes instead. Introduced by 350af54 (GH-14475). --- NEWS | 3 ++ ext/opcache/jit/zend_jit_trace.c | 2 +- ext/opcache/tests/jit/gh17626.inc | 2 ++ ext/opcache/tests/jit/gh17626.phpt | 41 ++++++++++++++++++++++ ext/opcache/tests/jit/gh17626_002.inc | 2 ++ ext/opcache/tests/jit/gh17626_002.phpt | 47 ++++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 ext/opcache/tests/jit/gh17626.inc create mode 100644 ext/opcache/tests/jit/gh17626.phpt create mode 100644 ext/opcache/tests/jit/gh17626_002.inc create mode 100644 ext/opcache/tests/jit/gh17626_002.phpt diff --git a/NEWS b/NEWS index 13b3286c7b67..dcdb83d1c0c6 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,9 @@ PHP NEWS . Fixed a tracing JIT crash when compiling a side trace for a method of a class that could not be stored in the inheritance cache. (GH-21710) (Arnaud, iliaal) + . Fixed bug GH-17626 (JIT corrupts an opline handler when blacklisting a + root trace at the opcache.jit_max_root_traces limit, causing spurious + "Too few arguments" errors and crashes). (RV7PR) - PDO: . Fixed a leak when a persistent connection failed a liveness check diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 6a3e8c3a4711..662fefe7dd7d 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -8780,7 +8780,7 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf SHM_UNPROTECT(); zend_jit_unprotect(); - ((zend_op*)opline)->handler = + ((zend_op*)(t->opline))->handler = ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->orig_handler; ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags &= ~ZEND_JIT_TRACE_JITED; diff --git a/ext/opcache/tests/jit/gh17626.inc b/ext/opcache/tests/jit/gh17626.inc new file mode 100644 index 000000000000..307a47ad2943 --- /dev/null +++ b/ext/opcache/tests/jit/gh17626.inc @@ -0,0 +1,2 @@ + +--EXPECT-- +Aa +Bb diff --git a/ext/opcache/tests/jit/gh17626_002.inc b/ext/opcache/tests/jit/gh17626_002.inc new file mode 100644 index 000000000000..8a12c7ae9fb6 --- /dev/null +++ b/ext/opcache/tests/jit/gh17626_002.inc @@ -0,0 +1,2 @@ +run('x'); +$a->run('x'); +$a->run('x'); +echo $a->run('x'), "\n"; +echo $b->run('y'), "\n"; +echo $b->run('y'), "\n"; +echo $a->run('x'), "\n"; +?> +--EXPECT-- +Ax +By +By +Ax