Skip to content

Commit 4af0dcc

Browse files
committed
Fix use-after-free in write_property when object is released
Fixes GH-10169
1 parent f7a28c4 commit 4af0dcc

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

Zend/tests/gh10169.phpt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
GH-10169
3+
--FILE--
4+
<?php
5+
class A
6+
{
7+
public string $prop;
8+
}
9+
class B
10+
{
11+
public function __toString()
12+
{
13+
global $a;
14+
$a = null;
15+
return str_repeat('a', 1);
16+
}
17+
}
18+
19+
$a = new A();
20+
try {
21+
$a->prop = new B();
22+
} catch (Error $e) {
23+
echo $e->getMessage(), "\n";
24+
}
25+
26+
$a = new A();
27+
$a->prop = '';
28+
try {
29+
$a->prop = new B();
30+
} catch (Error $e) {
31+
echo $e->getMessage(), "\n";
32+
}
33+
34+
?>
35+
--EXPECT--
36+
Object was released while assigning property A::$prop
37+
Object was released while assigning property A::$prop

Zend/zend_object_handlers.c

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,20 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
819819
}
820820

821821
ZVAL_COPY_VALUE(&tmp, value);
822-
if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()))) {
822+
// Increase refcount to prevent object from being released in __toString()
823+
GC_ADDREF(zobj);
824+
bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types());
825+
if (UNEXPECTED(GC_DELREF(zobj) == 0)) {
826+
zend_throw_error(NULL, "Object was released while assigning property %s::$%s",
827+
ZSTR_VAL(prop_info->ce->name), zend_get_unmangled_property_name(prop_info->name));
828+
zend_objects_store_del(zobj);
829+
zval_ptr_dtor(&tmp);
830+
variable_ptr = &EG(error_zval);
831+
goto exit;
832+
} else if (UNEXPECTED(GC_MAY_LEAK((zend_refcounted *)zobj))) {
833+
gc_possible_root((zend_refcounted *)zobj);
834+
}
835+
if (UNEXPECTED(!type_matched)) {
823836
Z_TRY_DELREF_P(value);
824837
variable_ptr = &EG(error_zval);
825838
goto exit;
@@ -890,7 +903,21 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
890903
}
891904

892905
ZVAL_COPY_VALUE(&tmp, value);
893-
if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()))) {
906+
907+
// Increase refcount to prevent object from being released in __toString()
908+
GC_ADDREF(zobj);
909+
bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types());
910+
if (UNEXPECTED(GC_DELREF(zobj) == 0)) {
911+
zend_throw_error(NULL, "Object was released while assigning property %s::$%s",
912+
ZSTR_VAL(prop_info->ce->name), zend_get_unmangled_property_name(prop_info->name));
913+
zend_objects_store_del(zobj);
914+
zval_ptr_dtor(&tmp);
915+
variable_ptr = &EG(error_zval);
916+
goto exit;
917+
} else if (UNEXPECTED(GC_MAY_LEAK((zend_refcounted *)zobj))) {
918+
gc_possible_root((zend_refcounted *)zobj);
919+
}
920+
if (UNEXPECTED(!type_matched)) {
894921
zval_ptr_dtor(value);
895922
goto exit;
896923
}

0 commit comments

Comments
 (0)