From 65cc61cdf8a522f059c5216fa7254daa019ec6a3 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 17 Jul 2025 19:07:15 +0200 Subject: [PATCH 1/2] Fix FETCH_OBJ_UNSET IS_UNDEF result UNSET_OBJ et al. do not expect to find IS_UNDEF results for IS_INDIRECT vars. To solve this, return IS_NULL from FETCH_OBJ_UNSET when properties are uninitialized. Do the same for FETCH_STATIC_PROP_IS, as we're otherwise copying IS_UNDEF into the VAR result, which is not a valid value for VAR. Fixes OSS-Fuzz #429429090 Closes GH-19160 (cherry picked from commit 8b4ef3a09f078a69a39ccf4429ed36382f559d4f) [Adapted to PHP-8.4, where zend_fetch_static_property_address_ex() returns zend_result: return FAILURE instead of NULL.] --- Zend/Optimizer/zend_inference.c | 2 +- Zend/tests/oss_fuzz_429429090.phpt | 20 ++++++++++++++++++++ Zend/zend_execute.c | 8 ++++++++ Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 2 +- 5 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 Zend/tests/oss_fuzz_429429090.phpt diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index dff89b16f9d4..97dfeb977293 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3849,7 +3849,7 @@ static zend_always_inline zend_result _zend_update_type_info( tmp &= ~MAY_BE_RC1; } if (opline->opcode == ZEND_FETCH_STATIC_PROP_IS) { - tmp |= MAY_BE_UNDEF; + tmp |= MAY_BE_NULL; } } UPDATE_SSA_TYPE(tmp, ssa_op->result_def); diff --git a/Zend/tests/oss_fuzz_429429090.phpt b/Zend/tests/oss_fuzz_429429090.phpt new file mode 100644 index 000000000000..d5279c2806ac --- /dev/null +++ b/Zend/tests/oss_fuzz_429429090.phpt @@ -0,0 +1,20 @@ +--TEST-- +OSS-Fuzz #429429090: FETCH_OBJ_UNSET IS_UNDEF result +--FILE-- +x[0]->prop); +unset($c->x[0]->prop); +isset(C::$y[0]->prop); +unset(C::$y[0]->prop); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index ae0b337ed2a6..d42f5220cfd1 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -3453,6 +3453,9 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } else if (UNEXPECTED(Z_ISERROR_P(ptr))) { ZVAL_ERROR(result); goto end; + } else if (type == BP_VAR_UNSET && UNEXPECTED(Z_TYPE_P(ptr) == IS_UNDEF)) { + ZVAL_NULL(result); + goto end; } ZVAL_INDIRECT(result, ptr); @@ -3603,6 +3606,11 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval return FAILURE; } + if (UNEXPECTED(Z_TYPE_P(*retval) == IS_UNDEF) + && (fetch_type == BP_VAR_IS || fetch_type == BP_VAR_UNSET)) { + return FAILURE; + } + *prop_info = property_info; if (EXPECTED(op1_type == IS_CONST) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index df255c5a868f..d0cf893778a3 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1839,7 +1839,7 @@ ZEND_VM_HELPER(zend_fetch_static_prop_helper, ANY, ANY, int type) SAVE_OPLINE(); if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) { - ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS)); + ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS) || (type == BP_VAR_UNSET)); prop = &EG(uninitialized_zval); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 4b313f20cd39..a8efecc3f9d5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -859,7 +859,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static SAVE_OPLINE(); if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) { - ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS)); + ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS) || (type == BP_VAR_UNSET)); prop = &EG(uninitialized_zval); } From cb55e293c6f9d730cf6d58f70c9f7197948947e9 Mon Sep 17 00:00:00 2001 From: Juan Versolato Lopes Date: Tue, 27 Oct 2026 11:44:29 -0300 Subject: [PATCH 2/2] Add regression test for GH-20403 Unsetting a property of an undefined dimension of an uninitialized typed static property passed a VAR operand to zval_undefined_cv(), which read past the end of the CV names. Fixed by the previous commit. --- Zend/tests/gh20403.phpt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Zend/tests/gh20403.phpt diff --git a/Zend/tests/gh20403.phpt b/Zend/tests/gh20403.phpt new file mode 100644 index 000000000000..8ac0c782b92b --- /dev/null +++ b/Zend/tests/gh20403.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-20403 (Heap buffer overflow in zval_undefined_cv() when unsetting through an uninitialized typed static property) +--FILE-- +x); + +?> +===DONE=== +--EXPECTF-- +Warning: Undefined variable $x in %s on line %d +===DONE===