Skip to content

Commit 7283338

Browse files
committed
Fix use-after-free on object released in hook
Fixes GH-16040
1 parent 6946bbc commit 7283338

File tree

2 files changed

+36
-22
lines changed

2 files changed

+36
-22
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
GH-16040: Use-after-free on object released in hook
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public $bar {
8+
get {
9+
$GLOBALS['a'] = null;
10+
return 42;
11+
}
12+
}
13+
}
14+
15+
$a = new A();
16+
var_dump($a->bar);
17+
18+
?>
19+
--EXPECT--
20+
int(42)

Zend/zend_object_handlers.c

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -684,22 +684,6 @@ static ZEND_COLD void zend_throw_no_prop_backing_value_access(zend_string *class
684684
ZSTR_VAL(class_name), ZSTR_VAL(prop_name));
685685
}
686686

687-
static bool zend_call_get_hook(
688-
const zend_property_info *prop_info, zend_string *prop_name,
689-
zend_function *get, zend_object *zobj, zval *rv)
690-
{
691-
if (!zend_should_call_hook(prop_info, zobj)) {
692-
if (UNEXPECTED(prop_info->flags & ZEND_ACC_VIRTUAL)) {
693-
zend_throw_no_prop_backing_value_access(zobj->ce->name, prop_name, /* is_read */ true);
694-
}
695-
return false;
696-
}
697-
698-
zend_call_known_instance_method_with_0_params(get, zobj, rv);
699-
700-
return true;
701-
}
702-
703687
ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int type, void **cache_slot, zval *rv) /* {{{ */
704688
{
705689
zval *retval;
@@ -808,8 +792,9 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
808792

809793
zend_class_entry *ce = zobj->ce;
810794

811-
if (!zend_call_get_hook(prop_info, name, get, zobj, rv)) {
812-
if (EG(exception)) {
795+
if (!zend_should_call_hook(prop_info, zobj)) {
796+
if (UNEXPECTED(prop_info->flags & ZEND_ACC_VIRTUAL)) {
797+
zend_throw_no_prop_backing_value_access(zobj->ce->name, name, /* is_read */ true);
813798
return &EG(uninitialized_zval);
814799
}
815800

@@ -826,12 +811,18 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
826811
goto try_again;
827812
}
828813

829-
if (EXPECTED(cache_slot
814+
bool cache_simple_get = cache_slot
830815
&& zend_execute_ex == execute_ex
831816
&& zobj->ce->default_object_handlers->read_property == zend_std_read_property
832817
&& !zobj->ce->create_object
833818
&& !zend_is_in_hook(prop_info)
834-
&& !(prop_info->hooks[ZEND_PROPERTY_HOOK_GET]->common.fn_flags & ZEND_ACC_RETURN_REFERENCE))) {
819+
&& !(prop_info->hooks[ZEND_PROPERTY_HOOK_GET]->common.fn_flags & ZEND_ACC_RETURN_REFERENCE);
820+
821+
zend_call_known_instance_method_with_0_params(get, zobj, rv);
822+
823+
/* Only cache simple get after calling the hook to provide better error
824+
* messages for accidentally recursive hooks. */
825+
if (EXPECTED(cache_simple_get)) {
835826
ZEND_SET_PROPERTY_HOOK_SIMPLE_GET(cache_slot);
836827
}
837828

@@ -2229,14 +2220,17 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has
22292220
}
22302221

22312222
zval rv;
2232-
if (!zend_call_get_hook(prop_info, name, get, zobj, &rv)) {
2233-
if (EG(exception)) {
2223+
if (!zend_should_call_hook(prop_info, zobj)) {
2224+
if (UNEXPECTED(prop_info->flags & ZEND_ACC_VIRTUAL)) {
2225+
zend_throw_no_prop_backing_value_access(zobj->ce->name, name, /* is_read */ true);
22342226
return 0;
22352227
}
22362228
property_offset = prop_info->offset;
22372229
goto try_again;
22382230
}
22392231

2232+
zend_call_known_instance_method_with_0_params(get, zobj, &rv);
2233+
22402234
if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY) {
22412235
result = zend_is_true(&rv);
22422236
} else {

0 commit comments

Comments
 (0)