Skip to content

Commit a0ee8e0

Browse files
committed
Optimizer should only rely on preloaded symbols in the symbol table
1 parent 88a361b commit a0ee8e0

File tree

6 files changed

+85
-17
lines changed

6 files changed

+85
-17
lines changed

Zend/Optimizer/zend_optimizer.c

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -792,20 +792,36 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_
792792
}
793793
}
794794

795-
static bool zend_optimizer_ignore_class(zend_class_entry *ce, zend_string *filename)
795+
static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename)
796796
{
797+
zend_class_entry *ce = Z_PTR_P(ce_zv);
798+
799+
if (ce->ce_flags & ZEND_ACC_PRELOADED) {
800+
Bucket *ce_bucket = (Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val));
801+
size_t offset = ce_bucket - EG(class_table)->arData;
802+
if (offset < EG(persistent_classes_count)) {
803+
return false;
804+
}
805+
}
797806
return ce->type == ZEND_USER_CLASS
798-
&& !(ce->ce_flags & ZEND_ACC_PRELOADED)
799807
&& (!ce->info.user.filename || ce->info.user.filename != filename);
800808
}
801809

802-
static bool zend_optimizer_ignore_function(zend_function *fbc, zend_string *filename)
810+
static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename)
803811
{
812+
zend_function *fbc = Z_PTR_P(fbc_zv);
813+
804814
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
805815
return false;
806816
} else if (fbc->type == ZEND_USER_FUNCTION) {
807-
return !(fbc->op_array.fn_flags & ZEND_ACC_PRELOADED)
808-
&& (!fbc->op_array.filename && fbc->op_array.filename != filename);
817+
if (fbc->op_array.fn_flags & ZEND_ACC_PRELOADED) {
818+
Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val));
819+
size_t offset = fbc_bucket - EG(function_table)->arData;
820+
if (offset < EG(persistent_functions_count)) {
821+
return false;
822+
}
823+
}
824+
return !fbc->op_array.filename || fbc->op_array.filename != filename;
809825
} else {
810826
ZEND_ASSERT(fbc->type == ZEND_EVAL_CODE);
811827
return true;
@@ -819,9 +835,9 @@ zend_class_entry *zend_optimizer_get_class_entry(
819835
return ce;
820836
}
821837

822-
ce = zend_hash_find_ptr(CG(class_table), lcname);
823-
if (ce && !zend_optimizer_ignore_class(ce, op_array ? op_array->filename : NULL)) {
824-
return ce;
838+
zval *ce_zv = zend_hash_find(CG(class_table), lcname);
839+
if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array ? op_array->filename : NULL)) {
840+
return Z_PTR_P(ce_zv);
825841
}
826842

827843
if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) {
@@ -862,9 +878,9 @@ const zend_class_constant *zend_fetch_class_const_info(
862878
if (script) {
863879
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1));
864880
} else {
865-
zend_class_entry *tmp = zend_hash_find_ptr(EG(class_table), Z_STR_P(op1 + 1));
866-
if (tmp != NULL && !zend_optimizer_ignore_class(tmp, op_array->filename)) {
867-
ce = tmp;
881+
zval *ce_zv = zend_hash_find(EG(class_table), Z_STR_P(op1 + 1));
882+
if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array->filename)) {
883+
ce = Z_PTR_P(ce_zv);
868884
}
869885
}
870886
}
@@ -909,11 +925,12 @@ zend_function *zend_optimizer_get_called_func(
909925
{
910926
zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2));
911927
zend_function *func;
928+
zval *func_zv;
912929
if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
913930
return func;
914-
} else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
915-
if (!zend_optimizer_ignore_function(func, op_array->filename)) {
916-
return func;
931+
} else if ((func_zv = zend_hash_find(EG(function_table), function_name)) != NULL) {
932+
if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) {
933+
return Z_PTR_P(func_zv);
917934
}
918935
}
919936
break;
@@ -923,11 +940,12 @@ zend_function *zend_optimizer_get_called_func(
923940
if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
924941
zval *function_name = CRT_CONSTANT(opline->op2) + 1;
925942
zend_function *func;
943+
zval *func_zv;
926944
if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
927945
return func;
928-
} else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
929-
if (!zend_optimizer_ignore_function(func, op_array->filename)) {
930-
return func;
946+
} else if ((func_zv = zend_hash_find(EG(function_table), Z_STR_P(function_name))) != NULL) {
947+
if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) {
948+
return Z_PTR_P(func_zv);
931949
}
932950
}
933951
}

ext/opcache/tests/gh15021.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-15021: Optimizer only relies on preloaded top-level symbols
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.preload={PWD}/gh15021_preload.inc
9+
--FILE--
10+
<?php
11+
putenv('RUNTIME=1');
12+
$firstRun = !isset(opcache_get_status()['scripts'][__DIR__ . DIRECTORY_SEPARATOR . 'gh15021_required.inc']);
13+
14+
if ($firstRun) {
15+
require __DIR__ . '/gh15021_a.inc';
16+
$expected = 1;
17+
} else {
18+
require __DIR__ . '/gh15021_b.inc';
19+
$expected = 2;
20+
}
21+
22+
require __DIR__ . '/gh15021_required.inc';
23+
24+
var_dump(f() === $expected);
25+
?>
26+
--EXPECT--
27+
bool(true)

ext/opcache/tests/gh15021_a.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
if (getenv('RUNTIME')) {
4+
function g(): int {
5+
return 1;
6+
}
7+
}

ext/opcache/tests/gh15021_b.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
if (getenv('RUNTIME')) {
4+
function g(): int {
5+
return 2;
6+
}
7+
}

ext/opcache/tests/gh15021_preload.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
opcache_compile_file(__DIR__ . '/gh15021_a.inc');
4+
opcache_compile_file(__DIR__ . '/gh15021_b.inc');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
function f(): int {
4+
return g();
5+
}

0 commit comments

Comments
 (0)