Skip to content

Commit 41e11a6

Browse files
committed
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.
1 parent 92a0cc7 commit 41e11a6

File tree

4 files changed

+61
-5
lines changed

4 files changed

+61
-5
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ PHP NEWS
3232
- Opcache:
3333
. Fixed bug GH-18417 (Windows SHM reattachment fails when increasing
3434
memory_consumption or jit_buffer_size). (nielsdos)
35+
. Fixed bug GH-18567 (Preloading with internal class alias triggers assertion
36+
failure). (nielsdos)
3537

3638
- PDO_OCI:
3739
. Fixed bug GH-18494 (PDO OCI segfault in statement GC). (nielsdos)

ext/opcache/ZendAccelerator.c

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3522,7 +3522,7 @@ static void preload_shutdown(void)
35223522
if (EG(class_table)) {
35233523
ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
35243524
zend_class_entry *ce = Z_PTR_P(zv);
3525-
if (ce->type == ZEND_INTERNAL_CLASS) {
3525+
if (ce->type == ZEND_INTERNAL_CLASS && Z_TYPE_P(zv) != IS_ALIAS_PTR) {
35263526
break;
35273527
}
35283528
} ZEND_HASH_MAP_FOREACH_END_DEL();
@@ -3610,7 +3610,15 @@ static void preload_move_user_classes(HashTable *src, HashTable *dst)
36103610
zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
36113611
ZEND_HASH_MAP_FOREACH_BUCKET_FROM(src, p, EG(persistent_classes_count)) {
36123612
zend_class_entry *ce = Z_PTR(p->val);
3613-
ZEND_ASSERT(ce->type == ZEND_USER_CLASS);
3613+
3614+
/* Possible with internal class aliases */
3615+
if (ce->type == ZEND_INTERNAL_CLASS) {
3616+
ZEND_ASSERT(Z_TYPE(p->val) == IS_ALIAS_PTR);
3617+
_zend_hash_append(dst, p->key, &p->val);
3618+
zend_hash_del_bucket(src, p);
3619+
continue;
3620+
}
3621+
36143622
if (ce->info.user.filename != filename) {
36153623
filename = ce->info.user.filename;
36163624
if (filename) {
@@ -3904,7 +3912,12 @@ static void preload_link(void)
39043912

39053913
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(EG(class_table), key, zv, EG(persistent_classes_count)) {
39063914
ce = Z_PTR_P(zv);
3907-
ZEND_ASSERT(ce->type != ZEND_INTERNAL_CLASS);
3915+
3916+
/* Possible with internal class aliases */
3917+
if (ce->type == ZEND_INTERNAL_CLASS) {
3918+
ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR);
3919+
continue;
3920+
}
39083921

39093922
if (!(ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
39103923
|| (ce->ce_flags & ZEND_ACC_LINKED)) {
@@ -3990,9 +4003,15 @@ static void preload_link(void)
39904003

39914004
ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
39924005
ce = Z_PTR_P(zv);
4006+
4007+
/* Possible with internal class aliases */
39934008
if (ce->type == ZEND_INTERNAL_CLASS) {
3994-
break;
4009+
if (Z_TYPE_P(zv) != IS_ALIAS_PTR) {
4010+
break; /* can stop already */
4011+
}
4012+
continue;
39954013
}
4014+
39964015
if ((ce->ce_flags & ZEND_ACC_LINKED) && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
39974016
if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */
39984017
CG(in_compilation) = true; /* prevent autoloading */
@@ -4009,7 +4028,13 @@ static void preload_link(void)
40094028
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(
40104029
EG(class_table), key, zv, EG(persistent_classes_count)) {
40114030
ce = Z_PTR_P(zv);
4012-
ZEND_ASSERT(ce->type != ZEND_INTERNAL_CLASS);
4031+
4032+
/* Possible with internal class aliases */
4033+
if (ce->type == ZEND_INTERNAL_CLASS) {
4034+
ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR);
4035+
continue;
4036+
}
4037+
40134038
if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
40144039
&& !(ce->ce_flags & ZEND_ACC_LINKED)) {
40154040
zend_string *lcname = zend_string_tolower(ce->name);

ext/opcache/tests/gh18567.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-18567 (Preloading with internal class alias triggers assertion failure)
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.preload={PWD}/preload_gh18567.inc
7+
--EXTENSIONS--
8+
opcache
9+
--SKIPIF--
10+
<?php
11+
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
12+
?>
13+
--FILE--
14+
<?php
15+
abstract class Test implements MyStringable {
16+
}
17+
$rc = new ReflectionClass(Test::class);
18+
var_dump($rc->getInterfaces());
19+
?>
20+
--EXPECT--
21+
array(1) {
22+
["Stringable"]=>
23+
object(ReflectionClass)#2 (1) {
24+
["name"]=>
25+
string(10) "Stringable"
26+
}
27+
}

ext/opcache/tests/preload_gh18567.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
class_alias('Stringable', 'MyStringable');

0 commit comments

Comments
 (0)