Skip to content

Commit 1e3d92f

Browse files
committed
Fix GH-14082: Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c
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. Closes GH-18916.
1 parent 8e731ca commit 1e3d92f

File tree

4 files changed

+81
-0
lines changed

4 files changed

+81
-0
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ PHP NEWS
2323
- Opcache:
2424
. Fixed bug GH-18639 (Internal class aliases can break preloading + JIT).
2525
(nielsdos)
26+
. Fixed bug GH-14082 (Segmentation fault on unknown address 0x600000000018
27+
in ext/opcache/jit/zend_jit.c). (nielsdos)
2628

2729
- Standard:
2830
. Fix misleading errors in printf(). (nielsdos)

ext/opcache/ZendAccelerator.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252

5353
#ifdef HAVE_JIT
5454
# include "jit/zend_jit.h"
55+
# include "Optimizer/zend_func_info.h"
56+
# include "Optimizer/zend_call_graph.h"
5557
#endif
5658

5759
#ifndef ZEND_WIN32
@@ -4363,6 +4365,39 @@ static void preload_load(void)
43634365
}
43644366
}
43654367

4368+
#if HAVE_JIT
4369+
static void zend_accel_clear_call_graph_ptrs(zend_op_array *op_array)
4370+
{
4371+
ZEND_ASSERT(ZEND_USER_CODE(op_array->type));
4372+
zend_func_info *info = ZEND_FUNC_INFO(op_array);
4373+
if (info) {
4374+
info->caller_info = NULL;
4375+
info->callee_info = NULL;
4376+
}
4377+
}
4378+
4379+
static void accel_reset_arena_info(zend_persistent_script *script)
4380+
{
4381+
zend_op_array *op_array;
4382+
zend_class_entry *ce;
4383+
4384+
zend_accel_clear_call_graph_ptrs(&script->script.main_op_array);
4385+
ZEND_HASH_MAP_FOREACH_PTR(&script->script.function_table, op_array) {
4386+
zend_accel_clear_call_graph_ptrs(op_array);
4387+
} ZEND_HASH_FOREACH_END();
4388+
ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4389+
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4390+
if (op_array->scope == ce
4391+
&& op_array->type == ZEND_USER_FUNCTION
4392+
&& !(op_array->fn_flags & ZEND_ACC_ABSTRACT)
4393+
&& !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4394+
zend_accel_clear_call_graph_ptrs(op_array);
4395+
}
4396+
} ZEND_HASH_FOREACH_END();
4397+
} ZEND_HASH_FOREACH_END();
4398+
}
4399+
#endif
4400+
43664401
static zend_result accel_preload(const char *config, bool in_child)
43674402
{
43684403
zend_file_handle file_handle;
@@ -4568,6 +4603,18 @@ static zend_result accel_preload(const char *config, bool in_child)
45684603
} ZEND_HASH_FOREACH_END();
45694604
ZCSG(saved_scripts)[i] = NULL;
45704605

4606+
#if HAVE_JIT
4607+
/* During persisting, the JIT may trigger and fill in the call graph.
4608+
* The call graph info is allocated on the arena which will be gone after preloading.
4609+
* To prevent invalid accesses during normal requests, the arena data should be cleared.
4610+
* This has to be done after all scripts have been persisted because shared op arrays between
4611+
* scripts can change the call graph. */
4612+
accel_reset_arena_info(ZCSG(preload_script));
4613+
for (zend_persistent_script **scripts = ZCSG(saved_scripts); *scripts; scripts++) {
4614+
accel_reset_arena_info(*scripts);
4615+
}
4616+
#endif
4617+
45714618
zend_shared_alloc_save_state();
45724619
accel_interned_strings_save_state();
45734620

ext/opcache/tests/jit/gh14082.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
GH-14082 (Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c)
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.jit=1235
7+
opcache.jit_buffer_size=16M
8+
opcache.preload={PWD}/preload_gh14082.inc
9+
--EXTENSIONS--
10+
opcache
11+
--SKIPIF--
12+
<?php
13+
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
14+
?>
15+
--FILE--
16+
<?php
17+
Foo::test();
18+
echo "ok\n";
19+
?>
20+
--EXPECT--
21+
int(1)
22+
int(1)
23+
ok
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
class Foo {
3+
public static function test() {
4+
static $i = 0;
5+
var_dump(++$i);
6+
}
7+
}
8+
9+
Foo::test();

0 commit comments

Comments
 (0)