Skip to content

Fix GH-24280: enforce asymmetric setter visibility in foreach by reference - #24592

Draft
Xauele wants to merge 1 commit into
php:PHP-8.4from
Xauele:fix/gh24280-upstream-candidate-20261017
Draft

Xauele wants to merge 1 commit into
php:PHP-8.4from
Xauele:fix/gh24280-upstream-candidate-20261017

Conversation

@Xauele

@Xauele Xauele commented Oct 23, 2026

Copy link
Copy Markdown

Fixes #24280: object foreach ($object as &$value) currently permits acquiring references to private(set) and protected(set) properties from forbidden scopes, including properties already held in a reference. Check setter visibility before exposing the reference, while retaining readonly checks and typed-reference tracking.

The existing setter-access helper handles protected scopes and fake scopes. A same-class fast path avoids the helper only when EG(fake_scope) is null and the executing function belongs to the declaring class.

Candidate: cf991cb, based on PHP-8.4 f6852be. Six files: VM source, regenerated executor, three PHPTs, NEWS. No CI harness is included.

Fresh native qualification: https://github.com/Xauele/php-src/actions/runs/39931975656 (source e15cea8; identical VM and PHPT blobs to this candidate; candidate additionally has NEWS).

  • PHP 8.4 NTS and ZTS: 4918 Zend tests passed per build, zero unexpected failures.
  • PHP 8.5: 5263 passed, zero unexpected failures.
  • PHP 8.6: 5523 passed, zero unexpected failures.
  • master (8.7-dev): 5530 passed, zero unexpected failures.
  • Every build reproduced four forbidden-scope bypasses before patching, then passed all three new PHPTs under the interpreter, OPcache, tracing JIT and function JIT. OPcache and JIT activation were asserted.
  • Each suite also has one pre-existing expected failure and version-dependent skips. These are pinned development branch builds, not a claim about every released patch version. PHP 8.3 and older do not support this setter syntax.

Tests cover fresh and retained references, forbidden global/child access, allowed owner/protected child writes, partial writes before an error, readonly/typed/dynamic/hooked properties, bound closure scopes, traits, global callees, lazy ghosts/proxies and 3000 repeated attempts.

Performance limitation: a release-build alternating A/B benchmark (20 samples per binary per case, same pinned base/configuration, interpreter, checked checksum) measured array -0.49%, stdClass +1.64%, plain declared properties +4.01%, typed +5.23%, retained references +1.97%, same-class restricted properties +6.35%. These are microbenchmark timings, not application-level estimates. The simpler helper-only variant repeatedly cost 11–12% in the last case. Raw samples are attached to the run as an artifact. The remaining cost needs maintainer assessment.

Full platform CI on this exact candidate: https://github.com/Xauele/php-src/actions/runs/39932513648 (pending). This is a draft for maintainer review, including assessment of the remaining measured hot-path overhead. Full platform CI is still pending; no merge readiness is claimed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant