Skip to content

fixes zend_type_release not freeing nested type lists #11884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Zend/tests/type_declarations/dnf_types/internal_dnf_property.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
DNF typed properties in internal classes
--EXTENSIONS--
zend_test
spl
reflection
--FILE--
<?php

$prop = new \ReflectionProperty(_ZendTestClass::class, 'dnfProperty');
$type = $prop->getType();
var_dump((string)$type);

$obj = new _ZendTestClass();
$obj->dnfProperty = new \ArrayIterator([1, 2, 3]);
try {
$obj->dnfProperty = [];
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
string(32) "Iterator|(Traversable&Countable)"
Cannot assign array to property _ZendTestClass::$dnfProperty of type Iterator|(Traversable&Countable)

4 changes: 4 additions & 0 deletions Zend/tests/type_declarations/typed_properties_095.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ object(_ZendTestClass)#1 (3) {
uninitialized(Traversable&Countable)
["readonlyProp"]=>
uninitialized(int)
["dnfProperty"]=>
uninitialized(Iterator|(Traversable&Countable))
}
int(123)
Cannot assign string to property _ZendTestClass::$intProp of type int
Expand All @@ -91,6 +93,8 @@ object(Test)#4 (3) {
uninitialized(Traversable&Countable)
["readonlyProp"]=>
uninitialized(int)
["dnfProperty"]=>
uninitialized(Iterator|(Traversable&Countable))
}
int(123)
Cannot assign string to property _ZendTestClass::$staticIntProp of type int
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -3496,7 +3496,7 @@ ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_nam
ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->properties_info, prop) {
if (prop->ce == disabled_class) {
zend_string_release(prop->name);
zend_type_release(prop->type, /* persistent */ 1);
zend_type_release_internal(prop->type);
free(prop);
}
} ZEND_HASH_FOREACH_END();
Expand Down
7 changes: 7 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,14 @@ ZEND_API void zend_destroy_static_vars(zend_op_array *op_array);
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);
/**
* Used to release userland types.
*/
ZEND_API void zend_type_release(zend_type type, bool persistent);
/**
* Used to release types associated with internal functions or classes.
*/
ZEND_API void zend_type_release_internal(zend_type type);
ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name);


Expand Down
27 changes: 25 additions & 2 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,28 @@ ZEND_API void zend_type_release(zend_type type, bool persistent) {
}
}

ZEND_API void zend_type_release_internal(zend_type type) {
ZEND_ASSERT(!ZEND_TYPE_USES_ARENA(type));
if (ZEND_TYPE_HAS_LIST(type)) {
zend_type *list_type, *sublist_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
if (ZEND_TYPE_HAS_LIST(*list_type)) {
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*list_type), sublist_type) {
if (ZEND_TYPE_HAS_NAME(*sublist_type)) {
zend_string_release(ZEND_TYPE_NAME(*sublist_type));
}
} ZEND_TYPE_LIST_FOREACH_END();
free(ZEND_TYPE_LIST(*list_type));
} else if (ZEND_TYPE_HAS_NAME(*list_type)) {
zend_string_release(ZEND_TYPE_NAME(*list_type));
}
} ZEND_TYPE_LIST_FOREACH_END();
free(ZEND_TYPE_LIST(type));
} else if (ZEND_TYPE_HAS_NAME(type)) {
zend_string_release(ZEND_TYPE_NAME(type));
}
}

void zend_free_internal_arg_info(zend_internal_function *function) {
if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
function->arg_info) {
Expand All @@ -142,7 +164,7 @@ void zend_free_internal_arg_info(zend_internal_function *function) {
num_args++;
}
for (i = 0 ; i < num_args; i++) {
zend_type_release(arg_info[i].type, /* persistent */ 1);
zend_type_release_internal(arg_info[i].type);
}
free(arg_info);
}
Expand Down Expand Up @@ -454,7 +476,7 @@ ZEND_API void destroy_zend_class(zval *zv)
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop_info) {
if (prop_info->ce == ce) {
zend_string_release(prop_info->name);
zend_type_release(prop_info->type, /* persistent */ 1);
zend_type_release_internal(prop_info->type);
free(prop_info);
}
} ZEND_HASH_FOREACH_END();
Expand Down Expand Up @@ -489,6 +511,7 @@ ZEND_API void destroy_zend_class(zval *zv)
} else {
zval_internal_ptr_dtor(&c->value);
}
zend_type_release_internal(c->type);
if (c->doc_comment) {
zend_string_release_ex(c->doc_comment, 1);
}
Expand Down
31 changes: 31 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -884,11 +884,42 @@ static void le_throwing_resource_dtor(zend_resource *rsrc)
zend_throw_exception(NULL, "Throwing resource destructor called", 0);
}

// We need to "manually" generate this property because gen_stubs.php
// does not support codegen for DNF types.
static void register_ZendTestClass_dnf_property(zend_class_entry *ce) {
zend_string *class_Iterator = zend_string_init_interned("Iterator", sizeof("Iterator") - 1, true);
zend_alloc_ce_cache(class_Iterator);
zend_string *class_Traversable = zend_string_init_interned("Traversable", sizeof("Traversable") - 1, true);
zend_alloc_ce_cache(class_Traversable);
zend_string *class_Countable = zend_string_init_interned("Countable", sizeof("Countable") - 1, true);
zend_alloc_ce_cache(class_Countable);
//
zend_type_list *intersection_type_list = malloc(ZEND_TYPE_LIST_SIZE(2));
intersection_type_list->num_types = 2;
intersection_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Traversable, 0, 0);
intersection_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Countable, 0, 0);
zend_type_list *union_type_list = malloc(ZEND_TYPE_LIST_SIZE(2));
union_type_list->num_types = 2;
union_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Iterator, 0, 0);
union_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_INTERSECTION(intersection_type_list, 0);
zend_type prop_type = (zend_type) ZEND_TYPE_INIT_UNION(union_type_list, 0);
//
zend_string *prop_name = zend_string_init_interned("dnfProperty", sizeof("dnfProperty") - 1, true);
zval default_value;
ZVAL_UNDEF(&default_value);
// We need this hack because some debug assertions incorrectly reject valid DNF types.
// see https://github.com/php/php-src/issues/10120#issuecomment-1360682804
zend_type hack = {0};
zend_property_info *prop = zend_declare_typed_property(ce, prop_name, &default_value, ZEND_ACC_PUBLIC, NULL, hack);
prop->type = prop_type;
}

PHP_MINIT_FUNCTION(zend_test)
{
zend_test_interface = register_class__ZendTestInterface();

zend_test_class = register_class__ZendTestClass(zend_test_interface);
register_ZendTestClass_dnf_property(zend_test_class);
zend_test_class->create_object = zend_test_class_new;
zend_test_class->get_static_method = zend_test_class_static_method_get;

Expand Down