]> granicus.if.org Git - php/commitdiff
Forbid use of not fully linked classes
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 22 May 2019 09:13:28 +0000 (11:13 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 23 May 2019 08:41:10 +0000 (10:41 +0200)
Zend/tests/bug30922.phpt
Zend/tests/use_unlinked_class.phpt [new file with mode: 0644]
Zend/zend_compile.h
Zend/zend_execute_API.c
Zend/zend_inheritance.c
Zend/zend_interfaces.c
Zend/zend_opcode.c

index 8da0f2192d82a5a87bb0007514029caebca27bae..0d5d8ae8385a54565baaac1c0645a9640339b3bd 100644 (file)
@@ -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 (file)
index 0000000..ed874ff
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+Classes can only be used once they are fully linked
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+    echo new ReflectionClass(A::class), "\n";
+});
+
+class A implements I {
+}
+
+?>
+--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
index 963314ced8152be5119b0523c35b91445c8314dc..998901160089c3a52fb23b337acae94ec2570786 100644 (file)
@@ -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)                       |     |     |     */
 /* ==============                                         |     |     |     */
 /*                                                        |     |     |     */
index fef14fa709196bf5c7fece6ae4ece9d245abd57d..0d5ceb86274c69faf59c30790ec3d7dbd6a04d14 100644 (file)
@@ -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
index 6ae32e562526a868841e11d51155bd928bcf7e2a..3f07fb46e818b53b6ea92e12f4d96ac5106ba7bf 100644 (file)
@@ -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;
 }
 /* }}} */
index fe4c9ed9a3528777ec2639d96364a0b3a42ed468..6c918c09fbac16ef05737424209ab2ef2be22eed 100644 (file)
@@ -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",
index 5ac5c8a859caf18af866b576069d8cd104e1ec35..980b40b58684dde16a6e7fd0a7c3ea81bf6cf69e 100644 (file)
@@ -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++) {