From 41e11a627da684c79f14295410bbf169092b1fc2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 16 May 2025 21:12:17 +0200 Subject: [PATCH] Fix GH-18567: Preloading with internal class alias triggers assertion failure The assertion is imprecise now, and the code assumed that from the moment an internal class was encountered that there were only internal classes remaining. This is wrong now, and we still have to continue if we encounter an internal class. We can only skip the remaining iterations if the entry in the hash table is not an alias. Closes GH-18575. --- NEWS | 2 ++ ext/opcache/ZendAccelerator.c | 35 +++++++++++++++++++++++---- ext/opcache/tests/gh18567.phpt | 27 +++++++++++++++++++++ ext/opcache/tests/preload_gh18567.inc | 2 ++ 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 ext/opcache/tests/gh18567.phpt create mode 100644 ext/opcache/tests/preload_gh18567.inc diff --git a/NEWS b/NEWS index f20891f344f..0e3b53db3c2 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,8 @@ PHP NEWS - Opcache: . Fixed bug GH-18417 (Windows SHM reattachment fails when increasing memory_consumption or jit_buffer_size). (nielsdos) + . Fixed bug GH-18567 (Preloading with internal class alias triggers assertion + failure). (nielsdos) - PDO_OCI: . Fixed bug GH-18494 (PDO OCI segfault in statement GC). (nielsdos) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index e3f7ca18151..47e0bc08bd5 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3522,7 +3522,7 @@ static void preload_shutdown(void) if (EG(class_table)) { ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { zend_class_entry *ce = Z_PTR_P(zv); - if (ce->type == ZEND_INTERNAL_CLASS) { + if (ce->type == ZEND_INTERNAL_CLASS && Z_TYPE_P(zv) != IS_ALIAS_PTR) { break; } } ZEND_HASH_MAP_FOREACH_END_DEL(); @@ -3610,7 +3610,15 @@ static void preload_move_user_classes(HashTable *src, HashTable *dst) zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); ZEND_HASH_MAP_FOREACH_BUCKET_FROM(src, p, EG(persistent_classes_count)) { zend_class_entry *ce = Z_PTR(p->val); - ZEND_ASSERT(ce->type == ZEND_USER_CLASS); + + /* Possible with internal class aliases */ + if (ce->type == ZEND_INTERNAL_CLASS) { + ZEND_ASSERT(Z_TYPE(p->val) == IS_ALIAS_PTR); + _zend_hash_append(dst, p->key, &p->val); + zend_hash_del_bucket(src, p); + continue; + } + if (ce->info.user.filename != filename) { filename = ce->info.user.filename; if (filename) { @@ -3904,7 +3912,12 @@ static void preload_link(void) ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(EG(class_table), key, zv, EG(persistent_classes_count)) { ce = Z_PTR_P(zv); - ZEND_ASSERT(ce->type != ZEND_INTERNAL_CLASS); + + /* Possible with internal class aliases */ + if (ce->type == ZEND_INTERNAL_CLASS) { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR); + continue; + } if (!(ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS)) || (ce->ce_flags & ZEND_ACC_LINKED)) { @@ -3990,9 +4003,15 @@ static void preload_link(void) ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { ce = Z_PTR_P(zv); + + /* Possible with internal class aliases */ if (ce->type == ZEND_INTERNAL_CLASS) { - break; + if (Z_TYPE_P(zv) != IS_ALIAS_PTR) { + break; /* can stop already */ + } + continue; } + if ((ce->ce_flags & ZEND_ACC_LINKED) && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */ CG(in_compilation) = true; /* prevent autoloading */ @@ -4009,7 +4028,13 @@ static void preload_link(void) ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM( EG(class_table), key, zv, EG(persistent_classes_count)) { ce = Z_PTR_P(zv); - ZEND_ASSERT(ce->type != ZEND_INTERNAL_CLASS); + + /* Possible with internal class aliases */ + if (ce->type == ZEND_INTERNAL_CLASS) { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR); + continue; + } + if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS)) && !(ce->ce_flags & ZEND_ACC_LINKED)) { zend_string *lcname = zend_string_tolower(ce->name); diff --git a/ext/opcache/tests/gh18567.phpt b/ext/opcache/tests/gh18567.phpt new file mode 100644 index 00000000000..e1d2a68af67 --- /dev/null +++ b/ext/opcache/tests/gh18567.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-18567 (Preloading with internal class alias triggers assertion failure) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.preload={PWD}/preload_gh18567.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- +getInterfaces()); +?> +--EXPECT-- +array(1) { + ["Stringable"]=> + object(ReflectionClass)#2 (1) { + ["name"]=> + string(10) "Stringable" + } +} diff --git a/ext/opcache/tests/preload_gh18567.inc b/ext/opcache/tests/preload_gh18567.inc new file mode 100644 index 00000000000..d277e83f83b --- /dev/null +++ b/ext/opcache/tests/preload_gh18567.inc @@ -0,0 +1,2 @@ +