[php-src] master: Merge branch 'PHP-8.4' into PHP-8.5
| From: | ndossche | Date: | Tue, 06 Oct 2026 20:17:21 +0000 |
| Subject: | [php-src] master: Merge branch 'PHP-8.4' into PHP-8.5 | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-142032@lists.php.net to get a copy of this message | ||
Author: ndossche (ndossche)
Date: 2026-10-06T22:16:16+02:00
Commit: https://github.com/php/php-src/commit/d083168cef0e4cc28dca1fde50678abb59cc2e1e
Raw diff: https://github.com/php/php-src/commit/d083168cef0e4cc28dca1fde50678abb59cc2e1e.diff
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
Fix GH-20890: Segfault in zval_undefined_cv with non-simple property hook with minimal tracing JIT
Changed paths:
A ext/opcache/tests/jit/gh20890.phpt
M NEWS
M ext/opcache/jit/zend_jit_ir.c
M ext/opcache/jit/zend_jit_trace.c
Diff:
diff --git a/NEWS b/NEWS
index 0f6bb34b368c..64235392b1bd 100644
--- a/NEWS
+++ b/NEWS
@@ -140,6 +140,8 @@ PHP NEWS
(ndossche)
. Fix SCCP evaluation of isset()/empty() on dimension of non-escaping object.
(ndossche)
+ . Fixed bug GH-20890 (Segfault in zval_undefined_cv with non-simple property
+ hook with minimal tracing JIT). (ndossche)
- OpenSSL:
. Fixed stream_socket_enable_crypto() leaving the socket non-blocking
diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index c91ad89da242..c10c904a5a66 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -17149,7 +17149,9 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array
*op_arr
opline->opcode == ZEND_DO_FCALL_BY_NAME ||
opline->opcode == ZEND_DO_FCALL ||
opline->opcode == ZEND_GENERATOR_CREATE ||
- opline->opcode == ZEND_INCLUDE_OR_EVAL) {
+ opline->opcode == ZEND_INCLUDE_OR_EVAL ||
+ /* May enter a simple get hook. */
+ opline->opcode == ZEND_FETCH_OBJ_R) {
jit_STORE_IP(jit, ir_AND_A(ref, ir_CONST_ADDR(~ZEND_VM_ENTER_BIT)));
} else {
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 5bb846b518f8..d78baf3838c3 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -335,6 +335,14 @@ static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op
// TODO: recompilation may change target ???
return 0;
#endif
+ case ZEND_FETCH_OBJ_R:
+ if (opline->op2_type == IS_CONST) {
+ const zend_class_entry *ce = opline->op1_type == IS_UNUSED ? op_array->scope : NULL;
+ if (!ce || !(ce->ce_flags & ZEND_ACC_FINAL) || ce->num_hooked_props > 0) {
+ return 1;
+ }
+ }
+ break;
case ZEND_RETURN_BY_REF:
case ZEND_RETURN:
/* return */
diff --git a/ext/opcache/tests/jit/gh20890.phpt b/ext/opcache/tests/jit/gh20890.phpt
new file mode 100644
index 000000000000..c375c379fcc7
--- /dev/null
+++ b/ext/opcache/tests/jit/gh20890.phpt
@@ -0,0 +1,37 @@
+--TEST--
+GH-20890 (Segfault in zval_undefined_cv with non-simple property hook with minimal tracing JIT)
+--CREDITS--
+Moonster8282
+--EXTENSIONS--
+opcache
+--INI--
+opcache.jit=1251
+--FILE--
+<?php
+class HookJIT {
+ private int $readCount = 0;
+
+ public int $computed {
+ get {
+ $this->readCount++;
+ return $this->readCount * 2;
+ }
+ }
+}
+
+function hook_hot_path($obj, $iterations) {
+ $sum = 0;
+ for ($i = 0; $i < $iterations; $i++) {
+ $sum += $obj->computed;
+ }
+ return $sum;
+}
+
+echo "Testing property hook in hot path...\n";
+$obj = new HookJIT();
+$result = hook_hot_path($obj, 100);
+echo "Result: $result\n";
+?>
+--EXPECT--
+Testing property hook in hot path...
+Result: 10100