From 2e006f862c1fbe555e38ca311a9f95e78335f97c Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 17 Jul 2026 14:20:13 +0200 Subject: [PATCH 1/2] Fix preloading FCC const exprs Preloading will try to evaluate class constants during compilation, but evaluated FCCs are objects, which can not be persisted. Apply the same fix as for enums: Fail evaluation when compiling. Preloading will also reset CG(map_ptr_last) after compilation, which results in collisions if map ptrs were allocated during compilation. Move the ZEND_MAP_PTR_NEW() to the persist phase, as a shared map ptr is not necessary before that. --- .../constexpr/gh22782.inc | 18 ++++++++++ .../constexpr/gh22782.phpt | 35 +++++++++++++++++++ Zend/zend_ast.c | 6 ++++ Zend/zend_compile.c | 1 - ext/opcache/zend_file_cache.c | 6 ++-- ext/opcache/zend_persist.c | 3 ++ 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 Zend/tests/first_class_callable/constexpr/gh22782.inc create mode 100644 Zend/tests/first_class_callable/constexpr/gh22782.phpt diff --git a/Zend/tests/first_class_callable/constexpr/gh22782.inc b/Zend/tests/first_class_callable/constexpr/gh22782.inc new file mode 100644 index 000000000000..d67ba4eab14e --- /dev/null +++ b/Zend/tests/first_class_callable/constexpr/gh22782.inc @@ -0,0 +1,18 @@ + +--FILE-- +getAttributes(A::class)[0]->newInstance()->fn)('hello')); +echo "# Method attr\n"; +var_dump((new ReflectionMethod(C::class, 'f')->getAttributes(A::class)[0]->newInstance()->fn)('hello')); +echo "# Method default value\n"; +var_dump((new C()->f())('hello')); + +?> +--EXPECT-- +# Class const +int(5) +# Class attr +int(5) +# Method attr +int(5) +# Method default value +int(5) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index bb405945849e..03ce073808ad 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1054,6 +1054,12 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( case ZEND_AST_CALL: case ZEND_AST_STATIC_CALL: { + // Preloading will attempt to resolve constants but objects can't be stored in shm + // Aborting here to store the const AST instead + if (CG(in_compilation)) { + return FAILURE; + } + zend_function *fptr; zend_class_entry *called_scope = NULL; switch (ast->kind) { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 3bda0ffadc32..d8d61ea979e4 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -11513,7 +11513,6 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr) if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) { zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); } - ZEND_MAP_PTR_NEW(((zend_ast_fcc *)*args_ast)->fptr); switch ((*ast_ptr)->kind) { case ZEND_AST_CALL: { diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index d430f4833d3c..fef6a5a5f42b 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -1303,7 +1303,9 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, zend_ast_get_op_array(ast)->op_array = Z_PTR(z); } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *fcc = (zend_ast_fcc*)ast; - ZEND_MAP_PTR_NEW(fcc->fptr); + if (!script->corrupted) { + ZEND_MAP_PTR_NEW(fcc->fptr); + } } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); @@ -2109,7 +2111,7 @@ void zend_file_cache_invalidate(zend_string *full_path) if (ZCG(accel_directives).file_cache_read_only) { return; } - + char *filename; filename = zend_file_cache_get_bin_file_path(full_path); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 29b6c2f7a6b2..a4aea9fae653 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -197,6 +197,9 @@ static zend_ast *zend_persist_ast(zend_ast *ast) node = (zend_ast *) copy; } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *copy = zend_shared_memdup(ast, sizeof(zend_ast_fcc)); + if (!ZCG(current_persistent_script)->corrupted) { + ZEND_MAP_PTR_NEW(copy->fptr); + } node = (zend_ast *) copy; } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ From 9418f21508959b971acf70689996018e18fc90b2 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 22 Jul 2026 09:41:24 +0200 Subject: [PATCH 2/2] [ci skip] NEWS --- NEWS | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index d989b44146d2..8181bf5def67 100644 --- a/NEWS +++ b/NEWS @@ -2,9 +2,16 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.10 +- Core: + . Fixed bug GH-22782 (Const expr FCC crashes under preloading). (Arnaud) + - Date: . Fixed leak on double DatePeriod::__construct() call. (ilutov) +- Opcache: + . Fixed bug GH-22763 (JIT fails to clear ZREG_TYPE_ONLY after setting reg). + (Arnaud) + - Sockets: . Fixed socket_set_option() validation error messages for UDP_SEGMENT and SO_LINGER options. (Weilin Du) @@ -117,7 +124,7 @@ PHP NEWS PHP 8.3 and PHP 8.5). (jorgsowa) - SPL: - . Fix class_parents for classes with leading slash in non-autoload mode. + . Fix class_parents for classes with leading slash in non-autoload mode. (jorgsowa) . Ignore leading back-slash in class_parents(), class_implements(), and class_uses(). (jorgsowa)