Skip to content

Commit 47ed190

Browse files
committed
Fix use-after-free in write_property when object is released
Fixes GH-10169 Closes GH-10179
1 parent fb670f2 commit 47ed190

File tree

4 files changed

+67
-2
lines changed

4 files changed

+67
-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: Fix use-after-free when releasing object during property assignment
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 to property A::$prop
37+
Object was released while assigning to property A::$prop

Zend/zend_execute.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,12 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(zen
899899
zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(type));
900900
}
901901

902+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_object_released_while_assigning_to_property_error(const zend_property_info *info)
903+
{
904+
zend_throw_error(NULL, "Object was released while assigning to property %s::$%s",
905+
ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name));
906+
}
907+
902908
static const zend_class_entry *resolve_single_class_type(zend_string *name, const zend_class_entry *self_ce) {
903909
if (zend_string_equals_literal_ci(name, "self")) {
904910
return self_ce;

Zend/zend_execute.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_readonly_property_indirect_modificati
8484

8585
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(zend_uchar type);
8686

87+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_object_released_while_assigning_to_property_error(const zend_property_info *info);
88+
8789
ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg);
8890
ZEND_API ZEND_COLD void zend_verify_arg_error(
8991
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value);

Zend/zend_object_handlers.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,17 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
818818
}
819819

820820
ZVAL_COPY_VALUE(&tmp, value);
821-
if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()))) {
821+
// Increase refcount to prevent object from being released in __toString()
822+
GC_ADDREF(zobj);
823+
bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types());
824+
if (UNEXPECTED(GC_DELREF(zobj) == 0)) {
825+
zend_object_released_while_assigning_to_property_error(prop_info);
826+
zend_objects_store_del(zobj);
827+
zval_ptr_dtor(&tmp);
828+
variable_ptr = &EG(error_zval);
829+
goto exit;
830+
}
831+
if (UNEXPECTED(!type_matched)) {
822832
Z_TRY_DELREF_P(value);
823833
variable_ptr = &EG(error_zval);
824834
goto exit;
@@ -889,7 +899,17 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
889899
}
890900

891901
ZVAL_COPY_VALUE(&tmp, value);
892-
if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()))) {
902+
// Increase refcount to prevent object from being released in __toString()
903+
GC_ADDREF(zobj);
904+
bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types());
905+
if (UNEXPECTED(GC_DELREF(zobj) == 0)) {
906+
zend_object_released_while_assigning_to_property_error(prop_info);
907+
zend_objects_store_del(zobj);
908+
zval_ptr_dtor(&tmp);
909+
variable_ptr = &EG(error_zval);
910+
goto exit;
911+
}
912+
if (UNEXPECTED(!type_matched)) {
893913
zval_ptr_dtor(value);
894914
goto exit;
895915
}

0 commit comments

Comments
 (0)