diff --git a/NEWS b/NEWS index 7bcfc484d73..cbf9dd7bd79 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ PHP NEWS SIMPLE_WRITE cache). (ilutov) . Fixed bug GH-17998 (Assignment to backing value in set hook of lazy proxy calls hook again). (ilutov) + . Fixed bug GH-17961 (use-after-free during dl()'ed module class destruction). + (Arnaud) + . Fixed bug GH-15367 (dl() of module with aliased class crashes in shutdown). + (Arnaud) - DBA: . Fixed assertion violation when opening the same file with dba_open diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 8c239a952f3..1021a6f7bd5 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -22,6 +22,7 @@ #include "zend.h" #include "zend_execute.h" #include "zend_API.h" +#include "zend_hash.h" #include "zend_modules.h" #include "zend_extensions.h" #include "zend_constants.h" @@ -3263,21 +3264,17 @@ ZEND_API zend_result zend_get_module_started(const char *module_name) /* {{{ */ } /* }}} */ -static int clean_module_class(zval *el, void *arg) /* {{{ */ -{ - zend_class_entry *ce = (zend_class_entry *)Z_PTR_P(el); - int module_number = *(int *)arg; - if (ce->type == ZEND_INTERNAL_CLASS && ce->info.internal.module->module_number == module_number) { - return ZEND_HASH_APPLY_REMOVE; - } else { - return ZEND_HASH_APPLY_KEEP; - } -} -/* }}} */ - static void clean_module_classes(int module_number) /* {{{ */ { - zend_hash_apply_with_argument(EG(class_table), clean_module_class, (void *) &module_number); + /* Child classes may reuse structures from parent classes, so destroy in reverse order. */ + Bucket *bucket; + ZEND_HASH_REVERSE_FOREACH_BUCKET(EG(class_table), bucket) { + zend_class_entry *ce = Z_CE(bucket->val); + if (ce->type == ZEND_INTERNAL_CLASS && ce->info.internal.module->module_number == module_number) { + zend_hash_del_bucket(EG(class_table), bucket); + } + } ZEND_HASH_FOREACH_END(); + } /* }}} */ diff --git a/ext/dl_test/dl_test.c b/ext/dl_test/dl_test.c index 1d411c000ce..7aebf543133 100644 --- a/ext/dl_test/dl_test.c +++ b/ext/dl_test/dl_test.c @@ -92,10 +92,22 @@ PHP_METHOD(DlTest, test) RETURN_STR(retval); } +PHP_METHOD(DlTestSuperClass, test) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_NULL(); +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(dl_test) { + zend_class_entry *ce; + register_class_DlTest(); + ce = register_class_DlTestSuperClass(); + register_class_DlTestSubClass(ce); + register_class_DlTestAliasedClass(); /* Test backwards compatibility */ if (getenv("PHP_DL_TEST_USE_OLD_REGISTER_INI_ENTRIES")) { diff --git a/ext/dl_test/dl_test.stub.php b/ext/dl_test/dl_test.stub.php index cd8b3916bae..c2d8ff57778 100644 --- a/ext/dl_test/dl_test.stub.php +++ b/ext/dl_test/dl_test.stub.php @@ -12,3 +12,15 @@ function dl_test_test2(string $str = ""): string {} class DlTest { public function test(string $str = ""): string {} } + +class DlTestSuperClass { + public int $a; + public function test(string $str = ""): string {} +} + +class DlTestSubClass extends DlTestSuperClass { +} + +/** @alias DlTestClassAlias */ +class DlTestAliasedClass { +} diff --git a/ext/dl_test/dl_test_arginfo.h b/ext/dl_test/dl_test_arginfo.h index 92d677acef3..84c7c151ae7 100644 --- a/ext/dl_test/dl_test_arginfo.h +++ b/ext/dl_test/dl_test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2dbacf5282b0f8e53923ac70495c2da43c7237e3 */ + * Stub hash: 0641a8eeff00e6c8083fe4a8639f970e3ba80db9 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test1, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -10,9 +10,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_DlTest_test arginfo_dl_test_test2 +#define arginfo_class_DlTestSuperClass_test arginfo_dl_test_test2 + ZEND_FUNCTION(dl_test_test1); ZEND_FUNCTION(dl_test_test2); ZEND_METHOD(DlTest, test); +ZEND_METHOD(DlTestSuperClass, test); static const zend_function_entry ext_functions[] = { ZEND_FE(dl_test_test1, arginfo_dl_test_test1) @@ -25,6 +28,11 @@ static const zend_function_entry class_DlTest_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_DlTestSuperClass_methods[] = { + ZEND_ME(DlTestSuperClass, test, arginfo_class_DlTestSuperClass_test, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_DlTest(void) { zend_class_entry ce, *class_entry; @@ -34,3 +42,40 @@ static zend_class_entry *register_class_DlTest(void) return class_entry; } + +static zend_class_entry *register_class_DlTestSuperClass(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DlTestSuperClass", class_DlTestSuperClass_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); + + zval property_a_default_value; + ZVAL_UNDEF(&property_a_default_value); + zend_string *property_a_name = zend_string_init("a", sizeof("a") - 1, 1); + zend_declare_typed_property(class_entry, property_a_name, &property_a_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_a_name); + + return class_entry; +} + +static zend_class_entry *register_class_DlTestSubClass(zend_class_entry *class_entry_DlTestSuperClass) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DlTestSubClass", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_DlTestSuperClass, 0); + + return class_entry; +} + +static zend_class_entry *register_class_DlTestAliasedClass(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DlTestAliasedClass", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); + zend_register_class_alias("DlTestClassAlias", class_entry); + + return class_entry; +}