Skip to content

Forbid use of not fully linked classes #4184

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 1 commit 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
2 changes: 1 addition & 1 deletion Zend/tests/bug30922.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
20 changes: 20 additions & 0 deletions Zend/tests/use_unlinked_class.phpt
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) | | | */
/* ============== | | | */
/* | | | */
Expand Down
6 changes: 5 additions & 1 deletion Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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;
}
/* }}} */
4 changes: 2 additions & 2 deletions Zend/zend_interfaces.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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++) {
Expand Down