From 48648388aefec01cbfbb893f8da690a976df386a Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 23 Feb 2026 22:25:10 +0100 Subject: [PATCH 1/3] Fix preloaded constant propagated to file-cached script Since GH-15021 preloaded constants are propagated to compiled scripts. This is problematic for file cache, which assumes all referenced zvals are either persistently allocated or local to the current script. However, preloaded constants live in shm as immutable, but not persistent. To solve this, we'd need to duplicate propagated constants in the optimizer when file cache is used. This is error prone given it needs to happen in many places. It's debatable whether constant propagation is even correct in this case, as running the preloaded script on a restart isn't guaranteed to produce the same result. Hence, avoid the issue for now by just not relying on preloaded symbols when file cache is used. Fixes GH-21052 --- Zend/Optimizer/zend_optimizer.c | 6 ++++++ ext/opcache/tests/gh21052.phpt | 24 ++++++++++++++++++++++++ ext/opcache/tests/gh21052_a.inc | 9 +++++++++ ext/opcache/tests/gh21052_b.inc | 4 ++++ 4 files changed, 43 insertions(+) create mode 100644 ext/opcache/tests/gh21052.phpt create mode 100644 ext/opcache/tests/gh21052_a.inc create mode 100644 ext/opcache/tests/gh21052_b.inc diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index 25f16d252a831..9017572118e28 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -799,6 +799,9 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename) zend_class_entry *ce = Z_PTR_P(ce_zv); if (ce->ce_flags & ZEND_ACC_PRELOADED) { + if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) { + return true; + } Bucket *ce_bucket = (Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val)); size_t offset = ce_bucket - EG(class_table)->arData; if (offset < EG(persistent_classes_count)) { @@ -817,6 +820,9 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename) return false; } else if (fbc->type == ZEND_USER_FUNCTION) { if (fbc->op_array.fn_flags & ZEND_ACC_PRELOADED) { + if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) { + return true; + } Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val)); size_t offset = fbc_bucket - EG(function_table)->arData; if (offset < EG(persistent_functions_count)) { diff --git a/ext/opcache/tests/gh21052.phpt b/ext/opcache/tests/gh21052.phpt new file mode 100644 index 0000000000000..9bf84817cfd1b --- /dev/null +++ b/ext/opcache/tests/gh21052.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-21052: Preloaded constant propagated to file-cached script +--CREDITS-- +Grummfy +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache="{TMP}" +opcache.preload={PWD}/gh21052_a.inc +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + string(3) "foo" +} +array(1) { + [0]=> + string(3) "foo" +} diff --git a/ext/opcache/tests/gh21052_a.inc b/ext/opcache/tests/gh21052_a.inc new file mode 100644 index 0000000000000..a1928a58b6173 --- /dev/null +++ b/ext/opcache/tests/gh21052_a.inc @@ -0,0 +1,9 @@ + Date: Mon, 23 Feb 2026 23:16:25 +0100 Subject: [PATCH 2/3] [skip ci] Add missing skipif for windows --- ext/opcache/tests/gh21052.phpt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/opcache/tests/gh21052.phpt b/ext/opcache/tests/gh21052.phpt index 9bf84817cfd1b..b28e3b6ad94c9 100644 --- a/ext/opcache/tests/gh21052.phpt +++ b/ext/opcache/tests/gh21052.phpt @@ -9,6 +9,10 @@ opcache.enable=1 opcache.enable_cli=1 opcache.file_cache="{TMP}" opcache.preload={PWD}/gh21052_a.inc +--SKIPIF-- + --FILE-- Date: Tue, 24 Feb 2026 09:38:19 +0100 Subject: [PATCH 3/3] Expand test for full test coverage --- ext/opcache/tests/gh21052.phpt | 4 ++++ ext/opcache/tests/gh21052_a.inc | 4 ++++ ext/opcache/tests/gh21052_b.inc | 1 + 3 files changed, 9 insertions(+) diff --git a/ext/opcache/tests/gh21052.phpt b/ext/opcache/tests/gh21052.phpt index b28e3b6ad94c9..377b76281a398 100644 --- a/ext/opcache/tests/gh21052.phpt +++ b/ext/opcache/tests/gh21052.phpt @@ -26,3 +26,7 @@ array(1) { [0]=> string(3) "foo" } +array(1) { + [0]=> + string(3) "foo" +} diff --git a/ext/opcache/tests/gh21052_a.inc b/ext/opcache/tests/gh21052_a.inc index a1928a58b6173..f9614369798b8 100644 --- a/ext/opcache/tests/gh21052_a.inc +++ b/ext/opcache/tests/gh21052_a.inc @@ -7,3 +7,7 @@ class A { return ['foo']; } } + +function test() { + return ['foo']; +} diff --git a/ext/opcache/tests/gh21052_b.inc b/ext/opcache/tests/gh21052_b.inc index 76b22f6dcdd83..cb64924ed5ae5 100644 --- a/ext/opcache/tests/gh21052_b.inc +++ b/ext/opcache/tests/gh21052_b.inc @@ -2,3 +2,4 @@ var_dump(A::C); var_dump(A::test()); +var_dump(test());