Skip to content

Fix GH-21687: array_walk creates references to readonly properties#21690

Open
iliaal wants to merge 1 commit intophp:masterfrom
iliaal:fix/gh-21687-enum-array-walk-assertion
Open

Fix GH-21687: array_walk creates references to readonly properties#21690
iliaal wants to merge 1 commit intophp:masterfrom
iliaal:fix/gh-21687-enum-array-walk-assertion

Conversation

@iliaal
Copy link
Copy Markdown
Contributor

@iliaal iliaal commented Apr 9, 2026

Fixes #21687

`array_walk()` wrapped object properties in `IS_REFERENCE` for the callback, bypassing readonly enforcement. For enum singletons this permanently corrupted the shared object (and opcache SHM), crashing on any subsequent `var_dump()`. For regular readonly properties it allowed the callback to silently modify values that should be immutable.

Checks `ZEND_ACC_READONLY` before creating the reference, matching the check that `foreach` by-reference already performs. Throws the same "Cannot acquire reference to readonly property" error.

Copy link
Copy Markdown
Member

@ndossche ndossche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. You're missing the point that it corrupts the enum and shm.

array_walk() wrapped object properties in IS_REFERENCE for the
callback, bypassing readonly enforcement. For enum singletons this
permanently corrupted the shared object (and opcache SHM), crashing
on any subsequent var_dump(). For regular readonly properties it
allowed the callback to silently modify values that should be
immutable.

Check ZEND_ACC_READONLY before creating the reference, matching the
check that foreach by-reference already performs. Throw the same
"Cannot acquire reference to readonly property" error.

Closes phpGH-21687
@iliaal iliaal force-pushed the fix/gh-21687-enum-array-walk-assertion branch from 56b2b9f to 1cbea32 Compare April 9, 2026 16:58
@iliaal iliaal requested a review from bukka as a code owner April 9, 2026 16:58
@iliaal iliaal changed the title Fix GH-21687: assertion failure in zend_enum_fetch_case_name after array_walk Fix GH-21687: array_walk creates references to readonly properties Apr 9, 2026
@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented Apr 9, 2026

Reworked. The fix is now in array_walk: check ZEND_ACC_READONLY before creating the reference, same check foreach already does. Enum properties are no longer corrupted, and regular readonly properties can no longer be silently modified through the callback.

@ndossche
Copy link
Copy Markdown
Member

ndossche commented Apr 9, 2026

Just from looking at the code I think that you can violate the type of a typed property, e.g. put a string where an int is expected.

@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented Apr 9, 2026

No. You're missing the point that it corrupts the enum and shm.

Was a little to quick fixing the symptom as opposed to the cause 🤦 Here goes attempt #2

@iliaal iliaal requested a review from ndossche April 9, 2026 18:02
ZVAL_NEW_REF(zv, zv);
ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
zend_throw_error(NULL,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the canonical message is Cannot indirectly modify readonly property Foo::$prop

E.g. from:

<?php

class Foo {
    public readonly int $prop;
    public function __construct() {
        $this->prop = 1;
    }
}

$foo = new Foo;
$ref =& $foo->prop;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foreach by-ref uses "Cannot acquire reference to readonly property", direct $ref =& $foo->prop uses "Cannot indirectly modify readonly property."

 class Foo {
      public readonly int $prop;
      public function __construct() { $this->prop = 1; }
  }
  $foo = new Foo;
  foreach ($foo as &$v) {}
  // Error: Cannot acquire reference to readonly property Foo::$prop

I matched the foreach wording since array_walk iterates properties and both are array related, but "Cannot indirectly modify" works too. @ndossche I can update the message, if you prefer, but there is a bit of inconsistency here

Copy link
Copy Markdown
Member

@ndossche ndossche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for me

@ndossche ndossche requested a review from iluuu1994 April 9, 2026 20:49
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.

Assertion failure in zend_enum_fetch_case_name() when calling array_walk() on an enum case value after try/catch block

2 participants