1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-20085: Assertion failure when combining lazy object get_properties exception with foreach loop

In this test, we will loop once, and then replace the object with an
instance that'll throw on property construction in Z_OBJPROP_P() in
the ZEND_FE_FETCH_RW VM handler.
Since at that point `pos >= fe_ht->nNumUsed`, we exit via
`fe_fetch_w_exit` without checking for an exception, causing incorrect
continuation of the code and an eventual assertion failure.

To solve this, we perform an exception check at the end of the
iteration. This should be sufficient to guarantee the exception is
checked in time as failure of get_properties() via Z_OBJPROP_P() will
always result in an empty hash table.
This should also be more efficient than the alternative fix that checks
for an exception right after Z_OBJPROP_P() as that would be executed at
each iteration.

Closes GH-20098.
This commit is contained in:
Niels Dossche
2025-10-07 21:16:22 +02:00
parent b47c3b633d
commit 27035eb01e
4 changed files with 31 additions and 2 deletions

2
NEWS
View File

@@ -6,6 +6,8 @@ PHP NEWS
. Fixed bug GH-19934 (CGI with auto_globals_jit=0 causes uouv). (ilutov)
. Fixed bug GH-20073 (Assertion failure in WeakMap offset operations on
reference). (nielsdos)
. Fixed bug GH-20085 (Assertion failure when combining lazy object
get_properties exception with foreach loop). (nielsdos)
09 Oct 2025, PHP 8.4.14

View File

@@ -0,0 +1,25 @@
--TEST--
GH-20085 (Assertion failure when combining lazy object get_properties exception with foreach loop)
--FILE--
<?php
class C {
public int $a;
public function __construct() {
$this->a = 1;
}
}
$obj = new C;
$reflector = new ReflectionClass(C::class);
foreach ($obj as &$value) {
$obj = $reflector->newLazyGhost(function ($obj) {
throw new Error;
});
}
echo !obj;
?>
--EXPECTF--
Fatal error: Uncaught Error in %s:%d
Stack trace:
#0 %s(%d): {closure:%s:%d}(Object(C))
#1 {main}
thrown in %s on line %d

View File

@@ -7275,7 +7275,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
ZEND_VM_C_GOTO(fe_fetch_w_exit);
ZEND_VM_C_GOTO(fe_fetch_w_exit_exc);
}
pos++;
value = &p->val;
@@ -7371,6 +7371,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
}
} else {
zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(array));
ZEND_VM_C_LABEL(fe_fetch_w_exit_exc):
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();

View File

@@ -23173,7 +23173,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
goto fe_fetch_w_exit;
goto fe_fetch_w_exit_exc;
}
pos++;
value = &p->val;
@@ -23269,6 +23269,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
}
} else {
zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(array));
fe_fetch_w_exit_exc:
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();