Commit c06e7370ae5 for php

commit c06e7370ae58ca8e132aae162c86bdaf40bc394d
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Sun Sep 13 14:18:59 2026 -0400

    ext/opcache: Fix GH-23679 tracing JIT shadowed private writes

    zend_get_known_property_info treated a ZEND_ACC_CHANGED public property as
    the known offset even when the executing scope owned a private property of
    the same name, so tracing JIT wrote through a child's public slot.
    Returning NULL uses the runtime-cache path, which already goes through
    zend_get_property_offset. FETCH_OBJ_W, ASSIGN_OBJ, INC, and ASSIGN_OBJ_OP
    share the helper; $this access, child methods, and unshadowed or
    protected-to-public properties do not hit this arm.

    Fixes GH-23679
    Closes GH-23683

diff --git a/NEWS b/NEWS
index 4f963f08f1f..23ce2db98b9 100644
--- a/NEWS
+++ b/NEWS
@@ -64,6 +64,10 @@ PHP                                                                        NEWS
   . Fixed OSS-Fuzz #5674034779193344 (Read of uninitialized memory in
     is_cacheable_stream_path()). (ndossche)
   . Fix zend_analyze_calls() call_stack buffer overrun. (Mrmaxmeier)
+  . Fixed bug GH-23628 (Tracing JIT reads undefined property slots of lazy
+    proxy objects instead of forwarding to the real instance). (lisachenko)
+  . Fixed bug GH-23679 (Tracing JIT writes a parent private property into a
+    child's shadowing public property). (Ilia Alshanetsky)

 - PDO:
   . Fixed PDOStatement::getColumnMeta() reading out of bounds for an invalid
@@ -98,10 +102,6 @@ PHP                                                                        NEWS
   . Fixed inflate_init() dropping the preset dictionary for raw streams with
     a non-default window. (Ilia Alshanetsky)

-- Opcache:
-  . Fixed bug GH-23628 (Tracing JIT reads undefined property slots of lazy
-    proxy objects instead of forwarding to the real instance). (lisachenko)
-

 24 Sep 2026, PHP 8.5.11

diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index 76510743d33..1e95d80456b 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -650,6 +650,11 @@ static zend_property_info* zend_get_known_property_info(const zend_op_array *op_
 	}

 	if (info->flags & ZEND_ACC_PUBLIC) {
+		if ((info->flags & ZEND_ACC_CHANGED)
+		 && op_array->scope
+		 && op_array->scope != ce) {
+			return NULL;
+		}
 		return info;
 	} else if (on_this) {
 		if (ce == info->ce) {
diff --git a/ext/opcache/tests/jit/gh23679.phpt b/ext/opcache/tests/jit/gh23679.phpt
new file mode 100644
index 00000000000..8b3c65c6567
--- /dev/null
+++ b/ext/opcache/tests/jit/gh23679.phpt
@@ -0,0 +1,116 @@
+--TEST--
+GH-23679: tracing JIT must not write a parent private property into a child's shadowing public property
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.protect_memory=1
+opcache.jit=tracing
+opcache.jit_hot_loop=1
+opcache.jit_hot_func=1
+opcache.jit_hot_return=1
+opcache.jit_hot_side_exit=1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+class A
+{
+    private $arr = [];
+    private $n = 0;
+
+    public function setDim($k, $v)
+    {
+        $x = clone $this;
+        $x->arr[$k] = $v;
+        return $x;
+    }
+
+    public function setAll($v)
+    {
+        $x = clone $this;
+        $x->arr = $v;
+        return $x;
+    }
+
+    public function inc()
+    {
+        $x = clone $this;
+        $x->n++;
+        return $x;
+    }
+
+    public function addN($k)
+    {
+        $x = clone $this;
+        $x->n += $k;
+        return $x;
+    }
+
+    public function getDim($k)
+    {
+        return $this->arr[$k] ?? 'MISSING';
+    }
+
+    public function getN()
+    {
+        return $this->n;
+    }
+}
+
+class B extends A
+{
+    public $arr = [];
+    public $n = 100;
+}
+
+for ($i = 0; $i < 50; $i++) {
+    (new A)->setDim('x', 1)->getDim('x');
+    (new A)->setAll(['x' => 1])->getDim('x');
+    (new A)->inc()->getN();
+    (new A)->addN(5)->getN();
+
+    $b = new B;
+    $b->arr = ['keep' => 1];
+    $b->n = 100;
+
+    $r = $b->setDim('x', 2);
+    if ($r->getDim('x') !== 2 || $r->arr !== ['keep' => 1]) {
+        echo "dim-assign i=$i private=";
+        var_dump($r->getDim('x'));
+        echo "dim-assign i=$i public=";
+        var_dump($r->arr);
+        exit(1);
+    }
+
+    $r = $b->setAll(['y' => 4]);
+    if ($r->getDim('y') !== 4 || $r->arr !== ['keep' => 1]) {
+        echo "assign i=$i private=";
+        var_dump($r->getDim('y'));
+        echo "assign i=$i public=";
+        var_dump($r->arr);
+        exit(1);
+    }
+
+    $r = $b->inc();
+    if ($r->getN() !== 1 || $r->n !== 100) {
+        echo "inc i=$i private=";
+        var_dump($r->getN());
+        echo "inc i=$i public=";
+        var_dump($r->n);
+        exit(1);
+    }
+
+    $r = $b->addN(5);
+    if ($r->getN() !== 5 || $r->n !== 100) {
+        echo "assign-op i=$i private=";
+        var_dump($r->getN());
+        echo "assign-op i=$i public=";
+        var_dump($r->n);
+        exit(1);
+    }
+}
+echo "ok\n";
+?>
+--EXPECT--
+ok