Commit 10ff41ed120 for php
commit 10ff41ed12034bb8a27aa4b7854f71f7bc2e9a1f
Author: lazerg <lazerg2@gmail.com>
Date: Sat Nov 7 06:01:44 2026 +0500
Fix GH-25253: JIT reads parent of an unlinked class (#25259)
zend_get_known_class() follows ce->parent for parent:: calls without
checking ZEND_ACC_LINKED. For an unlinked class whose parent is declared in
another file, this interprets parent_name as a class entry. A persisted
string can pass the subsequent type and filename checks, causing
zend_hash_find() to access invalid memory.
Check ZEND_ACC_LINKED before reading ce->parent, as the optimizer already
does. Add a regression test using opcache.interned_strings_buffer=0 to
reproduce the shared-memory layout that triggers the crash.
Co-authored-by: mlenarciak <39970740+mlenarciak@users.noreply.github.com>
Closes #25259
diff --git a/NEWS b/NEWS
index 2ce93fa3739..8cc13383530 100644
--- a/NEWS
+++ b/NEWS
@@ -40,6 +40,8 @@ PHP NEWS
SHM backend). (Lazizbek Ergashev)
. Fixed bug GH-25269 (JIT: misaligned zval passed to
ZEND_ADD_ARRAY_ELEMENT handler). (Jorg Sowa, ndossche)
+ . Fixed bug GH-25253 (JIT reads parent of an unlinked class).
+ (lazerg, mlenarciak)
- SOAP:
. Fixed bug GH-25267 (NULL dereference in to_xml_any() with integer key).
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index 1490c43e5b7..ff016ad8ff1 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -592,7 +592,7 @@ static zend_class_entry* zend_get_known_class(const zend_op_array *op_array, con
ZEND_ASSERT((op.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT);
ce = op_array->scope;
if (ce) {
- if (ce->parent) {
+ if ((ce->ce_flags & ZEND_ACC_LINKED) && ce->parent) {
ce = ce->parent;
if (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename) {
ce = NULL;
diff --git a/ext/opcache/tests/jit/gh25253.inc b/ext/opcache/tests/jit/gh25253.inc
new file mode 100644
index 00000000000..64db08b7513
--- /dev/null
+++ b/ext/opcache/tests/jit/gh25253.inc
@@ -0,0 +1,10 @@
+<?php
+
+class GH25253Child extends GH25253ParentClass {
+ public function m0() { return parent::m0(); }
+ public function m1() { return parent::m1(); }
+ public function m2() { return parent::m2(); }
+ public function m3() { return parent::m3(); }
+ public function m4() { return parent::m4(); }
+ public function m5() { return parent::m5(); }
+}
diff --git a/ext/opcache/tests/jit/gh25253.phpt b/ext/opcache/tests/jit/gh25253.phpt
new file mode 100644
index 00000000000..eb6c7c525ff
--- /dev/null
+++ b/ext/opcache/tests/jit/gh25253.phpt
@@ -0,0 +1,30 @@
+--TEST--
+GH-25253: JIT reads parent of an unlinked class
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.interned_strings_buffer=0
+opcache.jit=function
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+
+class GH25253ParentClass {
+ public function m0() { return 'm0'; }
+ public function m1() { return 'm1'; }
+ public function m2() { return 'm2'; }
+ public function m3() { return 'm3'; }
+ public function m4() { return 'm4'; }
+ public function m5() { return 'm5'; }
+}
+
+require __DIR__ . '/gh25253.inc';
+
+$c = new GH25253Child;
+echo $c->m0(), $c->m5(), "\n";
+
+?>
+--EXPECT--
+m0m5