Fix GH-17736: Assertion failure zend_reference_destroy()

The cache slot for FETCH_OBJ_W in function `test` is primed with the
class for C. The next call uses a simplexml instance and reuses the same
cache slot. simplexml's get_property_ptr handler does not use the cache
slot, so the old values remain in the cache slot. When
`zend_handle_fetch_obj_flags` is called this is not guarded by a check
for the class entry. So we end up using the prop_info from the property
C::$a instead of the simplexml property.

This patch adds a reset to the cache slots in the property address fetch
code and also in the extensions with a non-standard reference handler.
This keeps the run time cache consistent and avoids the issue without
complicating the fast paths.

Closes GH-17739.
This commit is contained in:
Niels Dossche 2025-02-08 12:44:58 +01:00
parent 6bb56fe0cf
commit ce8ab5f16a
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
11 changed files with 37 additions and 0 deletions

2
NEWS
View file

@ -2,6 +2,8 @@ PHP NEWS
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.3.19 ?? ??? ????, PHP 8.3.19
- Treewide:
. Fixed bug GH-17736 (Assertion failure zend_reference_destroy()). (nielsdos)
27 Feb 2025, PHP 8.3.18RC1 27 Feb 2025, PHP 8.3.18RC1

View file

@ -3232,6 +3232,9 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
return; return;
} }
} }
} else if (prop_op_type == IS_CONST) {
/* CE mismatch, make cache slot consistent */
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
} }
/* Pointer on property callback is required */ /* Pointer on property callback is required */

View file

@ -4399,6 +4399,7 @@ static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string
zend_string_equals_literal(name, "days") || zend_string_equals_literal(name, "days") ||
zend_string_equals_literal(name, "invert") ) { zend_string_equals_literal(name, "invert") ) {
/* Fallback to read_property. */ /* Fallback to read_property. */
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
ret = NULL; ret = NULL;
} else { } else {
ret = zend_std_get_property_ptr_ptr(object, name, type, cache_slot); ret = zend_std_get_property_ptr_ptr(object, name, type, cache_slot);

View file

@ -303,6 +303,7 @@ static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, in
return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
} }
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
return NULL; return NULL;
} }

View file

@ -2495,6 +2495,7 @@ static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name
ZEND_IGNORE_VALUE(type); ZEND_IGNORE_VALUE(type);
ZEND_IGNORE_VALUE(cache_slot); ZEND_IGNORE_VALUE(cache_slot);
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
return NULL; return NULL;
} }

View file

@ -635,6 +635,8 @@ static zval *sxe_property_get_adr(zend_object *object, zend_string *zname, int f
SXE_ITER type; SXE_ITER type;
zval member; zval member;
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
sxe = php_sxe_fetch_object(object); sxe = php_sxe_fetch_object(object);
GET_NODE(sxe, node); GET_NODE(sxe, node);
if (UNEXPECTED(!node)) { if (UNEXPECTED(!node)) {

View file

@ -0,0 +1,20 @@
--TEST--
GH-17736 (Assertion failure zend_reference_destroy())
--EXTENSIONS--
simplexml
--FILE--
<?php
$o1 = new SimpleXMLElement('<a/>');
class C {
public int $a = 1;
}
function test($obj) {
$ref =& $obj->a;
}
$obj = new C;
test($obj);
test($o1);
echo "Done\n";
?>
--EXPECT--
Done

View file

@ -1861,6 +1861,7 @@ static zval *php_snmp_get_property_ptr_ptr(zend_object *object, zend_string *nam
return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
} }
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
return NULL; return NULL;
} }

View file

@ -844,6 +844,8 @@ static zval *spl_array_get_property_ptr_ptr(zend_object *object, zend_string *na
if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0 if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
&& !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) { && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) {
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
/* If object has offsetGet() overridden, then fallback to read_property, /* If object has offsetGet() overridden, then fallback to read_property,
* which will call offsetGet(). */ * which will call offsetGet(). */
zval member; zval member;

View file

@ -121,6 +121,8 @@ zval *xmlreader_get_property_ptr_ptr(zend_object *object, zend_string *name, int
zval *retval = NULL; zval *retval = NULL;
xmlreader_prop_handler *hnd = NULL; xmlreader_prop_handler *hnd = NULL;
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
obj = php_xmlreader_fetch_object(object); obj = php_xmlreader_fetch_object(object);
if (obj->prop_handler != NULL) { if (obj->prop_handler != NULL) {

View file

@ -890,6 +890,8 @@ static zval *php_zip_get_property_ptr_ptr(zend_object *object, zend_string *name
zval *retval = NULL; zval *retval = NULL;
zip_prop_handler *hnd = NULL; zip_prop_handler *hnd = NULL;
cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
obj = php_zip_fetch_object(object); obj = php_zip_fetch_object(object);
if (obj->prop_handler != NULL) { if (obj->prop_handler != NULL) {