diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index bd6b323871bcc..1b0101dbfd6cb 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -52,6 +52,8 @@ #ifdef HAVE_JIT # include "jit/zend_jit.h" +# include "Optimizer/zend_func_info.h" +# include "Optimizer/zend_call_graph.h" #endif #ifndef ZEND_WIN32 @@ -4363,6 +4365,39 @@ static void preload_load(void) } } +#if HAVE_JIT +static void zend_accel_clear_call_graph_ptrs(zend_op_array *op_array) +{ + ZEND_ASSERT(ZEND_USER_CODE(op_array->type)); + zend_func_info *info = ZEND_FUNC_INFO(op_array); + if (info) { + info->caller_info = NULL; + info->callee_info = NULL; + } +} + +static void accel_reset_arena_info(zend_persistent_script *script) +{ + zend_op_array *op_array; + zend_class_entry *ce; + + zend_accel_clear_call_graph_ptrs(&script->script.main_op_array); + ZEND_HASH_MAP_FOREACH_PTR(&script->script.function_table, op_array) { + zend_accel_clear_call_graph_ptrs(op_array); + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) { + ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->scope == ce + && op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_ABSTRACT) + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + zend_accel_clear_call_graph_ptrs(op_array); + } + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); +} +#endif + static zend_result accel_preload(const char *config, bool in_child) { zend_file_handle file_handle; @@ -4568,6 +4603,18 @@ static zend_result accel_preload(const char *config, bool in_child) } ZEND_HASH_FOREACH_END(); ZCSG(saved_scripts)[i] = NULL; +#if HAVE_JIT + /* During persisting, the JIT may trigger and fill in the call graph. + * The call graph info is allocated on the arena which will be gone after preloading. + * To prevent invalid accesses during normal requests, the arena data should be cleared. + * This has to be done after all scripts have been persisted because shared op arrays between + * scripts can change the call graph. */ + accel_reset_arena_info(ZCSG(preload_script)); + for (zend_persistent_script **scripts = ZCSG(saved_scripts); *scripts; scripts++) { + accel_reset_arena_info(*scripts); + } +#endif + zend_shared_alloc_save_state(); accel_interned_strings_save_state(); diff --git a/ext/opcache/tests/jit/gh14082.phpt b/ext/opcache/tests/jit/gh14082.phpt new file mode 100644 index 0000000000000..eba67a096b82c --- /dev/null +++ b/ext/opcache/tests/jit/gh14082.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-14082 (Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1235 +opcache.jit_buffer_size=16M +opcache.preload={PWD}/preload_gh14082.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(1) +int(1) +ok diff --git a/ext/opcache/tests/jit/preload_gh14082.inc b/ext/opcache/tests/jit/preload_gh14082.inc new file mode 100644 index 0000000000000..f5b11ac621ebe --- /dev/null +++ b/ext/opcache/tests/jit/preload_gh14082.inc @@ -0,0 +1,9 @@ +