From 1206e47860e029a2a3a956f507d526f4f580d63d Mon Sep 17 00:00:00 2001 From: Tom Atkinson Date: Fri, 7 Aug 2026 16:03:03 +0200 Subject: [PATCH] opcache: set ZEND_ACC_PRELOADED for compile-only preloaded enums GH-17835 made zend_enum_register_func() tag an enum's generated cases()/from()/tryFrom() with ZEND_ACC_PRELOADED, so that zend_persist_class_method() gives them a static run_time_cache map_ptr, which survives into every request. The tag is set inside the if (EG(active)) branch. An enum that preload reaches through opcache_compile_file() rather than by executing its file is linked from preload_link(), which runs after the preload request has ended, so EG(active) is already false and the tag is never applied. Those functions get a non-static map_ptr that nothing initialises again, so RUN_TIME_CACHE() is NULL in a fresh request and the first observed call dereferences NULL + the observer handle: #0 zend_observer_fcall_begin_specialized Zend/zend_observer.h:92 #1 ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER Zend/zend_vm_execute.h:2281 Move the tag out of the EG(active) branch so both linking paths set it. The compile-only case then takes exactly the same route as the already fixed declared case. --- NEWS | 2 ++ Zend/zend_enum.c | 6 ++-- .../tests/preload_enum_compile_only.inc | 3 ++ .../preload_enum_observed_compile_only.phpt | 30 +++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 ext/opcache/tests/preload_enum_compile_only.inc create mode 100644 ext/opcache/tests/preload_enum_observed_compile_only.phpt diff --git a/NEWS b/NEWS index 474db936ef15..62353d3107b2 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,8 @@ PHP NEWS - Opcache: . Fixed bug GH-22857 (Function JIT emits wrong code for FETCH_OBJ_FUNC_ARG on a property hook getter, losing register-held variables). (Zhao Hao) + . Fixed crash in observer API calling cases() on an enum preloaded with + opcache_compile_file(). (Thomas Atkinson) - OpenSSL: . Fix missing error check on invalid alpn protocols. (ndossche) diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c index 464e7d801a89..e1190d4e3289 100644 --- a/Zend/zend_enum.c +++ b/Zend/zend_enum.c @@ -418,10 +418,10 @@ static void zend_enum_register_func(zend_class_entry *ce, zend_known_string_id n zif->module = EG(current_module); zif->scope = ce; zif->T = ZEND_OBSERVER_ENABLED; + if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) { + zif->fn_flags |= ZEND_ACC_PRELOADED; + } if (EG(active)) { // at run-time - if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) { - zif->fn_flags |= ZEND_ACC_PRELOADED; - } ZEND_MAP_PTR_INIT(zif->run_time_cache, zend_arena_calloc(&CG(arena), 1, zend_internal_run_time_cache_reserved_size())); } else { #ifdef ZTS diff --git a/ext/opcache/tests/preload_enum_compile_only.inc b/ext/opcache/tests/preload_enum_compile_only.inc new file mode 100644 index 000000000000..fd7eeddd932e --- /dev/null +++ b/ext/opcache/tests/preload_enum_compile_only.inc @@ -0,0 +1,3 @@ + +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + enum(MyEnum::Foo) + [1]=> + enum(MyEnum::Bar) +}