From: Nikita Popov Date: Wed, 22 May 2019 09:13:28 +0000 (+0200) Subject: Forbid use of not fully linked classes X-Git-Tag: php-7.4.0alpha1~208 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=64918c770282c0f60b407e8de3201a6b68c88e78;p=php Forbid use of not fully linked classes --- diff --git a/Zend/tests/bug30922.phpt b/Zend/tests/bug30922.phpt index 8da0f2192d..0d5d8ae838 100644 --- a/Zend/tests/bug30922.phpt +++ b/Zend/tests/bug30922.phpt @@ -10,4 +10,4 @@ var_dump($a instanceOf A); echo "ok\n"; ?> --EXPECTF-- -Fatal error: Interface RecurisiveFooFar cannot implement itself in %sbug30922.php on line %d +Fatal error: Interface 'RecurisiveFooFar' not found in %sbug30922.php on line %d diff --git a/Zend/tests/use_unlinked_class.phpt b/Zend/tests/use_unlinked_class.phpt new file mode 100644 index 0000000000..ed874ff101 --- /dev/null +++ b/Zend/tests/use_unlinked_class.phpt @@ -0,0 +1,20 @@ +--TEST-- +Classes can only be used once they are fully linked +--FILE-- + +--EXPECTF-- +Fatal error: During class fetch: Uncaught ReflectionException: Class A does not exist in %s:%d +Stack trace: +#0 %s(%d): ReflectionClass->__construct('A') +#1 [internal function]: {closure}('I') +#2 %s(%d): spl_autoload_call('I') +#3 {main} in %s on line %d diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 963314ced8..9989011600 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -269,6 +269,9 @@ typedef struct _zend_oparray_context { /* Children must reuse parent get_iterator() | | | */ #define ZEND_ACC_REUSE_GET_ITERATOR (1 << 18) /* X | | | */ /* | | | */ +/* Class is being linked. Don't free strings. | | | */ +#define ZEND_ACC_LINKING_IN_PROGRESS (1 << 19) /* X | | | */ +/* | | | */ /* Function Flags (unused: 28...30) | | | */ /* ============== | | | */ /* | | | */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index fef14fa709..0d5ceb8627 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -915,7 +915,11 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * if (!key) { zend_string_release_ex(lc_name, 0); } - return (zend_class_entry*)Z_PTR_P(zv); + ce = (zend_class_entry*)Z_PTR_P(zv); + if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) { + return NULL; + } + return ce; } /* The compiler is not-reentrant. Make sure we __autoload() only during run-time diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 6ae32e5625..3f07fb46e8 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2018,7 +2018,7 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent) /* {{{ */ { - ce->ce_flags |= ZEND_ACC_LINKED; + ce->ce_flags |= ZEND_ACC_LINKING_IN_PROGRESS; if (parent) { zend_do_inheritance(ce, parent); } @@ -2033,5 +2033,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent) } zend_build_properties_info_table(ce); + ce->ce_flags &= ~ZEND_ACC_LINKING_IN_PROGRESS; + ce->ce_flags |= ZEND_ACC_LINKED; } /* }}} */ diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index fe4c9ed9a3..6c918c09fb 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -291,7 +291,7 @@ static int zend_implement_traversable(zend_class_entry *interface, zend_class_en return SUCCESS; } if (class_type->num_interfaces) { - ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_LINKED); + ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS)); for (i = 0; i < class_type->num_interfaces; i++) { if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) { return SUCCESS; @@ -321,7 +321,7 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr } else if (class_type->get_iterator != zend_user_it_get_new_iterator) { /* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */ if (class_type->num_interfaces) { - ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_LINKED); + ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS)); for (i = 0; i < class_type->num_interfaces; i++) { if (class_type->interfaces[i] == zend_ce_iterator) { zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time", diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 5ac5c8a859..980b40b586 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -238,7 +238,7 @@ ZEND_API void destroy_zend_class(zval *zv) } switch (ce->type) { case ZEND_USER_CLASS: - if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { + if (ce->parent_name && !(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) { zend_string_release_ex(ce->parent_name, 0); } if (ce->default_properties_table) { @@ -298,7 +298,7 @@ ZEND_API void destroy_zend_class(zval *zv) } zend_hash_destroy(&ce->constants_table); if (ce->num_interfaces > 0) { - if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + if (!(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) { uint32_t i; for (i = 0; i < ce->num_interfaces; i++) {