Skip to content

Commit fbf4a95

Browse files
committed
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.
1 parent 2edb369 commit fbf4a95

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
GH-20085 (Assertion failure when combining lazy object get_properties exception with foreach loop)
3+
--FILE--
4+
<?php
5+
class C {
6+
public int $a;
7+
public function __construct() {
8+
$this->a = 1;
9+
}
10+
}
11+
$obj = new C;
12+
$reflector = new ReflectionClass(C::class);
13+
foreach ($obj as &$value) {
14+
$obj = $reflector->newLazyGhost(function ($obj) {
15+
throw new Error;
16+
});
17+
}
18+
echo !obj;
19+
?>
20+
--EXPECTF--
21+
Fatal error: Uncaught Error in %s:%d
22+
Stack trace:
23+
#0 %s(%d): {closure:%s:%d}(Object(C))
24+
#1 {main}
25+
thrown in %s on line %d

Zend/zend_vm_def.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7275,7 +7275,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
72757275
while (1) {
72767276
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
72777277
/* reached end of iteration */
7278-
ZEND_VM_C_GOTO(fe_fetch_w_exit);
7278+
ZEND_VM_C_GOTO(fe_fetch_w_exit_exc);
72797279
}
72807280
pos++;
72817281
value = &p->val;
@@ -7371,6 +7371,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
73717371
}
73727372
} else {
73737373
zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(array));
7374+
ZEND_VM_C_LABEL(fe_fetch_w_exit_exc):
73747375
if (UNEXPECTED(EG(exception))) {
73757376
UNDEF_RESULT();
73767377
HANDLE_EXCEPTION();

Zend/zend_vm_execute.h

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)