diff --git a/Zend/tests/gh21776.phpt b/Zend/tests/gh21776.phpt new file mode 100644 index 000000000000..4f6ae956a226 --- /dev/null +++ b/Zend/tests/gh21776.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-21776 (Heap use-after-free in zend_object_is_lazy via magic __isset) +--FILE-- +a ?? 0; +echo "OK\n"; +?> +--EXPECT-- +OK diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 1a2c70fac1e0..5c281caef75e 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -742,6 +742,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int uintptr_t property_offset; const zend_property_info *prop_info = NULL; uint32_t *guard = NULL; + bool release_zobj = false; #if DEBUG_OBJECT_HANDLERS fprintf(stderr, "Read object #%d property: %s\n", zobj->handle, ZSTR_VAL(name)); @@ -936,7 +937,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int if (zobj->ce->__get && !((*guard) & IN_GET)) { goto call_getter; } - OBJ_RELEASE(zobj); + release_zobj = true; } else if (zobj->ce->__get && !((*guard) & IN_GET)) { goto call_getter_addref; } @@ -998,11 +999,12 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int (*guard) |= guard_type; retval = zend_std_read_property(instance, name, type, cache_slot, rv); (*guard) &= ~guard_type; - return retval; + goto exit; } } - return zend_std_read_property(instance, name, type, cache_slot, rv); + retval = zend_std_read_property(instance, name, type, cache_slot, rv); + goto exit; } } if (type != BP_VAR_IS) { @@ -1015,6 +1017,9 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int retval = &EG(uninitialized_zval); exit: + if (release_zobj) { + OBJ_RELEASE(zobj); + } return retval; } /* }}} */