[php-src] master: Merge branch 'PHP-8.6'
| From: | ndossche | Date: | Wed, 07 Oct 2026 20:26:49 +0000 |
| Subject: | [php-src] master: Merge branch 'PHP-8.6' | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-142075@lists.php.net to get a copy of this message | ||
Author: ndossche (ndossche)
Date: 2026-10-07T22:26:29+02:00
Commit: https://github.com/php/php-src/commit/60702faf6af0b0f93fc1f2cc27318acf8b359f7d
Raw diff: https://github.com/php/php-src/commit/60702faf6af0b0f93fc1f2cc27318acf8b359f7d.diff
Merge branch 'PHP-8.6'
* PHP-8.6:
Fix GH-17626: JIT corrupts opline handler when blacklisting root trace
Changed paths:
A ext/opcache/tests/jit/gh17626.inc
A ext/opcache/tests/jit/gh17626.phpt
A ext/opcache/tests/jit/gh17626_002.inc
A ext/opcache/tests/jit/gh17626_002.phpt
M ext/opcache/jit/zend_jit_trace.c
Diff:
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 748ede31a0a0..e4c59d0b8aa7 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -8890,7 +8890,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 @@
+<?php
+function gh17626_callee(string $s) { return strtoupper($s); }
diff --git a/ext/opcache/tests/jit/gh17626.phpt b/ext/opcache/tests/jit/gh17626.phpt
new file mode 100644
index 000000000000..dbd917935c5f
--- /dev/null
+++ b/ext/opcache/tests/jit/gh17626.phpt
@@ -0,0 +1,41 @@
+--TEST--
+GH-17626: Opline handler corrupted when a root trace is blacklisted at the max_root_traces limit
(fails with --repeat 2)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.revalidate_freq=0
+opcache.jit=tracing
+opcache.jit_buffer_size=16M
+opcache.jit_hot_func=2
+opcache.jit_hot_loop=255
+opcache.jit_hot_return=255
+opcache.jit_hot_side_exit=255
+opcache.jit_max_root_traces=2
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+namespace GH17626;
+
+// In --repeat 2 the callee is recompiled after the first run, so the function
+// guard in the trace compiled for caller() fails with ZEND_JIT_EXIT_INVALIDATE.
+
+require __DIR__ . '/gh17626.inc';
+
+function caller(string $s) {
+ return gh17626_callee($s) . $s;
+}
+
+caller('a');
+caller('a');
+caller('a');
+echo caller('a'), "\n";
+echo caller('b'), "\n";
+
+touch(__DIR__ . '/gh17626.inc');
+opcache_invalidate(__DIR__ . '/gh17626.inc', true);
+?>
+--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 @@
+<?php
+class GH17626GrandParent {}
diff --git a/ext/opcache/tests/jit/gh17626_002.phpt b/ext/opcache/tests/jit/gh17626_002.phpt
new file mode 100644
index 000000000000..a0f4792f5404
--- /dev/null
+++ b/ext/opcache/tests/jit/gh17626_002.phpt
@@ -0,0 +1,47 @@
+--TEST--
+GH-17626: Opline handler corrupted when a root trace is blacklisted at the max_root_traces limit
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit=tracing
+opcache.jit_buffer_size=16M
+opcache.jit_hot_func=2
+opcache.jit_hot_loop=255
+opcache.jit_hot_return=255
+opcache.jit_hot_side_exit=255
+opcache.jit_max_root_traces=2
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+require __DIR__ . '/gh17626_002.inc';
+
+class ParentA extends GH17626GrandParent { public static function m() { return 'A'; } }
+class ParentB extends GH17626GrandParent { public static function m() { return 'B'; } }
+
+trait T {
+ public function run(string $s) {
+ return parent::m() . $s;
+ }
+}
+
+class A extends ParentA { use T; }
+class B extends ParentB { use T; }
+
+$a = new A;
+$b = new B;
+
+$a->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