Skip to content

Commit 64918c7

Browse files
committed
Forbid use of not fully linked classes
1 parent fdb7fc6 commit 64918c7

File tree

7 files changed

+36
-7
lines changed

7 files changed

+36
-7
lines changed

Zend/tests/bug30922.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ var_dump($a instanceOf A);
1010
echo "ok\n";
1111
?>
1212
--EXPECTF--
13-
Fatal error: Interface RecurisiveFooFar cannot implement itself in %sbug30922.php on line %d
13+
Fatal error: Interface 'RecurisiveFooFar' not found in %sbug30922.php on line %d

Zend/tests/use_unlinked_class.phpt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Classes can only be used once they are fully linked
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
echo new ReflectionClass(A::class), "\n";
8+
});
9+
10+
class A implements I {
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: During class fetch: Uncaught ReflectionException: Class A does not exist in %s:%d
16+
Stack trace:
17+
#0 %s(%d): ReflectionClass->__construct('A')
18+
#1 [internal function]: {closure}('I')
19+
#2 %s(%d): spl_autoload_call('I')
20+
#3 {main} in %s on line %d

Zend/zend_compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ typedef struct _zend_oparray_context {
269269
/* Children must reuse parent get_iterator() | | | */
270270
#define ZEND_ACC_REUSE_GET_ITERATOR (1 << 18) /* X | | | */
271271
/* | | | */
272+
/* Class is being linked. Don't free strings. | | | */
273+
#define ZEND_ACC_LINKING_IN_PROGRESS (1 << 19) /* X | | | */
274+
/* | | | */
272275
/* Function Flags (unused: 28...30) | | | */
273276
/* ============== | | | */
274277
/* | | | */

Zend/zend_execute_API.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,11 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
915915
if (!key) {
916916
zend_string_release_ex(lc_name, 0);
917917
}
918-
return (zend_class_entry*)Z_PTR_P(zv);
918+
ce = (zend_class_entry*)Z_PTR_P(zv);
919+
if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) {
920+
return NULL;
921+
}
922+
return ce;
919923
}
920924

921925
/* The compiler is not-reentrant. Make sure we __autoload() only during run-time

Zend/zend_inheritance.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2018,7 +2018,7 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
20182018

20192019
ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent) /* {{{ */
20202020
{
2021-
ce->ce_flags |= ZEND_ACC_LINKED;
2021+
ce->ce_flags |= ZEND_ACC_LINKING_IN_PROGRESS;
20222022
if (parent) {
20232023
zend_do_inheritance(ce, parent);
20242024
}
@@ -2033,5 +2033,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent)
20332033
}
20342034

20352035
zend_build_properties_info_table(ce);
2036+
ce->ce_flags &= ~ZEND_ACC_LINKING_IN_PROGRESS;
2037+
ce->ce_flags |= ZEND_ACC_LINKED;
20362038
}
20372039
/* }}} */

Zend/zend_interfaces.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ static int zend_implement_traversable(zend_class_entry *interface, zend_class_en
291291
return SUCCESS;
292292
}
293293
if (class_type->num_interfaces) {
294-
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_LINKED);
294+
ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
295295
for (i = 0; i < class_type->num_interfaces; i++) {
296296
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
297297
return SUCCESS;
@@ -321,7 +321,7 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr
321321
} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
322322
/* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */
323323
if (class_type->num_interfaces) {
324-
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_LINKED);
324+
ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
325325
for (i = 0; i < class_type->num_interfaces; i++) {
326326
if (class_type->interfaces[i] == zend_ce_iterator) {
327327
zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",

Zend/zend_opcode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ ZEND_API void destroy_zend_class(zval *zv)
238238
}
239239
switch (ce->type) {
240240
case ZEND_USER_CLASS:
241-
if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
241+
if (ce->parent_name && !(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) {
242242
zend_string_release_ex(ce->parent_name, 0);
243243
}
244244
if (ce->default_properties_table) {
@@ -298,7 +298,7 @@ ZEND_API void destroy_zend_class(zval *zv)
298298
}
299299
zend_hash_destroy(&ce->constants_table);
300300
if (ce->num_interfaces > 0) {
301-
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
301+
if (!(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) {
302302
uint32_t i;
303303

304304
for (i = 0; i < ce->num_interfaces; i++) {

0 commit comments

Comments
 (0)