Skip to content

Commit 848a8f2

Browse files
committed
Optimizer should only rely on preloaded symbols in the symbol table
1 parent 0223a8d commit 848a8f2

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
@@ -793,20 +793,36 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_
793793
}
794794
}
795795

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

803-
static bool zend_optimizer_ignore_function(zend_function *fbc, zend_string *filename)
811+
static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename)
804812
{
813+
zend_function *fbc = Z_PTR_P(fbc_zv);
814+
805815
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
806816
return false;
807817
} else if (fbc->type == ZEND_USER_FUNCTION) {
808-
return !(fbc->op_array.fn_flags & ZEND_ACC_PRELOADED)
809-
&& (!fbc->op_array.filename && fbc->op_array.filename != filename);
818+
if (fbc->op_array.fn_flags & ZEND_ACC_PRELOADED) {
819+
Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val));
820+
size_t offset = fbc_bucket - EG(function_table)->arData;
821+
if (offset < EG(persistent_functions_count)) {
822+
return false;
823+
}
824+
}
825+
return !fbc->op_array.filename || fbc->op_array.filename != filename;
810826
} else {
811827
ZEND_ASSERT(fbc->type == ZEND_EVAL_CODE);
812828
return true;
@@ -820,9 +836,9 @@ zend_class_entry *zend_optimizer_get_class_entry(
820836
return ce;
821837
}
822838

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

828844
if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) {
@@ -863,9 +879,9 @@ const zend_class_constant *zend_fetch_class_const_info(
863879
if (script) {
864880
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1));
865881
} else {
866-
zend_class_entry *tmp = zend_hash_find_ptr(EG(class_table), Z_STR_P(op1 + 1));
867-
if (tmp != NULL && !zend_optimizer_ignore_class(tmp, op_array->filename)) {
868-
ce = tmp;
882+
zval *ce_zv = zend_hash_find(EG(class_table), Z_STR_P(op1 + 1));
883+
if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array->filename)) {
884+
ce = Z_PTR_P(ce_zv);
869885
}
870886
}
871887
}
@@ -910,11 +926,12 @@ zend_function *zend_optimizer_get_called_func(
910926
{
911927
zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2));
912928
zend_function *func;
929+
zval *func_zv;
913930
if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
914931
return func;
915-
} else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
916-
if (!zend_optimizer_ignore_function(func, op_array->filename)) {
917-
return func;
932+
} else if ((func_zv = zend_hash_find(EG(function_table), function_name)) != NULL) {
933+
if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) {
934+
return Z_PTR_P(func_zv);
918935
}
919936
}
920937
break;
@@ -924,11 +941,12 @@ zend_function *zend_optimizer_get_called_func(
924941
if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
925942
zval *function_name = CRT_CONSTANT(opline->op2) + 1;
926943
zend_function *func;
944+
zval *func_zv;
927945
if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
928946
return func;
929-
} else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
930-
if (!zend_optimizer_ignore_function(func, op_array->filename)) {
931-
return func;
947+
} else if ((func_zv = zend_hash_find(EG(function_table), Z_STR_P(function_name))) != NULL) {
948+
if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) {
949+
return Z_PTR_P(func_zv);
932950
}
933951
}
934952
}

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)