Skip to content

Commit f948370

Browse files
committed
Merge branch 'PHP-8.1'
* PHP-8.1: Add JIT guards for INIT_FCALL instructions and functions that may be modified
2 parents d2bb59c + 6c25413 commit f948370

19 files changed

+543
-12
lines changed

ext/opcache/jit/zend_jit_arm64.dasc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8709,7 +8709,8 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t
87098709
| // Get the return value of function zend_jit_find_func_helper/zend_jit_find_ns_func_helper
87108710
| mov REG0, RETVALx
87118711
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8712-
int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
8712+
int32_t exit_point = zend_jit_trace_get_exit_point(opline,
8713+
func && (func->common.fn_flags & ZEND_ACC_IMMUTABLE) ? ZEND_JIT_EXIT_INVALIDATE : 0);
87138714
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
87148715

87158716
if (!exit_addr) {

ext/opcache/jit/zend_jit_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ typedef enum _zend_jit_trace_stop {
412412
#define ZEND_JIT_EXIT_PACKED_GUARD (1<<7)
413413
#define ZEND_JIT_EXIT_CLOSURE_CALL (1<<8) /* exit because of polymorphic INIT_DYNAMIC_CALL call */
414414
#define ZEND_JIT_EXIT_METHOD_CALL (1<<9) /* exit because of polymorphic INIT_METHOD_CALL call */
415+
#define ZEND_JIT_EXIT_INVALIDATE (1<<10) /* invalidate current trace */
415416

416417
typedef union _zend_op_trace_info {
417418
zend_op dummy; /* the size of this structure must be the same as zend_op */
@@ -584,6 +585,7 @@ typedef struct _zend_jit_trace_info {
584585
uint32_t flags; /* See ZEND_JIT_TRACE_... defines above */
585586
uint32_t polymorphism; /* Counter of polymorphic calls */
586587
uint32_t jmp_table_size;/* number of jmp_table slots */
588+
const zend_op_array *op_array; /* function */
587589
const zend_op *opline; /* first opline */
588590
const void *code_start; /* address of native code */
589591
zend_jit_trace_exit_info *exit_info; /* info about side exits */

ext/opcache/jit/zend_jit_trace.c

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,26 @@ static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op
365365
return 0;
366366
}
367367

368+
static bool zend_jit_may_be_modified(const zend_function *func, const zend_op_array *called_from)
369+
{
370+
if (func->type == ZEND_INTERNAL_FUNCTION) {
371+
#ifdef _WIN32
372+
/* ASLR */
373+
return 1;
374+
#else
375+
return 0;
376+
#endif
377+
} else if (func->type == ZEND_USER_FUNCTION) {
378+
if (func->common.fn_flags & ZEND_ACC_PRELOADED) {
379+
return 0;
380+
}
381+
if (func->op_array.filename == called_from->filename && !func->op_array.scope) {
382+
return 0;
383+
}
384+
}
385+
return 1;
386+
}
387+
368388
static zend_always_inline uint32_t zend_jit_trace_type_to_info_ex(zend_uchar type, uint32_t info)
369389
{
370390
if (type == IS_UNKNOWN) {
@@ -6085,10 +6105,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
60856105
if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
60866106
goto jit_failure;
60876107
}
6088-
if ((opline->opcode != ZEND_INIT_STATIC_METHOD_CALL
6108+
if ((p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func
6109+
&& (opline->opcode != ZEND_INIT_STATIC_METHOD_CALL
60896110
|| opline->op1_type != IS_CONST
6090-
|| opline->op2_type != IS_CONST)
6091-
&& (p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func) {
6111+
|| opline->op2_type != IS_CONST
6112+
|| zend_jit_may_be_modified((p+1)->func, op_array))) {
60926113
if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {
60936114
goto jit_failure;
60946115
}
@@ -6098,8 +6119,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
60986119
if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
60996120
goto jit_failure;
61006121
}
6101-
if (opline->op2_type != IS_CONST
6102-
&& (p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func) {
6122+
if ((p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func
6123+
&& (opline->op2_type != IS_CONST
6124+
|| zend_jit_may_be_modified((p+1)->func, op_array))) {
61036125
if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {
61046126
goto jit_failure;
61056127
}
@@ -6109,8 +6131,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
61096131
if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
61106132
goto jit_failure;
61116133
}
6112-
if (opline->op1_type != IS_CONST
6113-
&& (p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func) {
6134+
if ((p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func
6135+
&& (opline->op1_type != IS_CONST
6136+
|| zend_jit_may_be_modified((p+1)->func, op_array))) {
61146137
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_OBJECT, 1);
61156138
if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {
61166139
goto jit_failure;
@@ -6689,8 +6712,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
66896712
call_info = call_info->next_callee;
66906713
}
66916714
if (!skip_guard
6692-
&& !zend_jit_may_be_polymorphic_call(init_opline)) {
6693-
// TODO: recompilation may change target ???
6715+
&& !zend_jit_may_be_polymorphic_call(init_opline)
6716+
&& !zend_jit_may_be_modified(p->func, op_array)) {
66946717
skip_guard = 1;
66956718
}
66966719
}
@@ -7023,6 +7046,7 @@ static zend_jit_trace_stop zend_jit_compile_root_trace(zend_jit_trace_rec *trace
70237046
t->flags = 0;
70247047
t->polymorphism = 0;
70257048
t->jmp_table_size = 0;
7049+
t->op_array = trace_buffer[0].op_array;
70267050
t->opline = trace_buffer[1].opline;
70277051
t->exit_info = exit_info;
70287052
t->stack_map = NULL;
@@ -8051,6 +8075,35 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
80518075
EX(opline)->lineno);
80528076
}
80538077

8078+
if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_INVALIDATE) {
8079+
zend_jit_op_array_trace_extension *jit_extension;
8080+
uint32_t num = trace_num;
8081+
8082+
while (t->root != num) {
8083+
num = t->root;
8084+
t = &zend_jit_traces[num];
8085+
}
8086+
8087+
SHM_UNPROTECT();
8088+
zend_jit_unprotect();
8089+
8090+
jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(t->op_array);
8091+
if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_LOOP) {
8092+
((zend_op*)(t->opline))->handler = (const void*)zend_jit_loop_trace_counter_handler;
8093+
} else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_ENTER) {
8094+
((zend_op*)(t->opline))->handler = (const void*)zend_jit_func_trace_counter_handler;
8095+
} else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_RETURN) {
8096+
((zend_op*)(t->opline))->handler = (const void*)zend_jit_ret_trace_counter_handler;
8097+
}
8098+
ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags &=
8099+
ZEND_JIT_TRACE_START_LOOP|ZEND_JIT_TRACE_START_ENTER|ZEND_JIT_TRACE_START_RETURN;
8100+
8101+
zend_jit_protect();
8102+
SHM_PROTECT();
8103+
8104+
return 0;
8105+
}
8106+
80548107
if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_TO_VM) {
80558108
if (zend_jit_trace_exit_is_bad(trace_num, exit_num)) {
80568109
zend_jit_blacklist_trace_exit(trace_num, exit_num);

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9319,13 +9319,13 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t
93199319
ZEND_UNREACHABLE();
93209320
}
93219321
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9322-
int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
9322+
int32_t exit_point = zend_jit_trace_get_exit_point(opline,
9323+
func && (func->common.fn_flags & ZEND_ACC_IMMUTABLE) ? ZEND_JIT_EXIT_INVALIDATE : 0);
93239324
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
93249325

93259326
if (!exit_addr) {
93269327
return 0;
93279328
}
9328-
93299329
if (!func || opline->opcode == ZEND_INIT_FCALL) {
93309330
| test r0, r0
93319331
| jnz >3

ext/opcache/tests/jit/gh8461-001.inc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
$x = 0;
4+
5+
class UniqueList
6+
{
7+
const A = 1;
8+
9+
public static function foo()
10+
{
11+
global $x;
12+
$x += self::A;
13+
}
14+
}

ext/opcache/tests/jit/gh8461-001.phpt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Bug GH-8461 001 (JIT does not account for class re-compile)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.jit_buffer_size=1M
9+
opcache.jit=1255
10+
opcache.file_update_protection=0
11+
opcache.revalidate_freq=0
12+
opcache.protect_memory=1
13+
--FILE--
14+
<?php
15+
16+
// Checks that JITed code does not crash in --repeat 2 after the UniqueList
17+
// class is recompiled.
18+
19+
require __DIR__ . '/gh8461-001.inc';
20+
21+
class UniqueListLast extends UniqueList
22+
{
23+
public static function bar() {
24+
parent::foo();
25+
}
26+
}
27+
28+
for ($i = 0; $i < 10; $i++) {
29+
UniqueListLast::bar();
30+
}
31+
32+
// mark the file as changed (important)
33+
touch(__DIR__ . '/gh8461-001.inc');
34+
35+
print "OK";
36+
--EXPECT--
37+
OK

ext/opcache/tests/jit/gh8461-002.inc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
$x = 0;
4+
5+
class UniqueList
6+
{
7+
const A = 1;
8+
9+
public static function foo()
10+
{
11+
global $x;
12+
$x += self::A;
13+
}
14+
}

ext/opcache/tests/jit/gh8461-002.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Bug GH-8461 002 (JIT does not account for class re-compile)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.jit_buffer_size=1M
9+
opcache.jit=1255
10+
opcache.file_update_protection=0
11+
opcache.revalidate_freq=0
12+
opcache.protect_memory=1
13+
--FILE--
14+
<?php
15+
16+
// Checks that JITed code does not crash in --repeat 2 after the UniqueList
17+
// class is recompiled.
18+
19+
require __DIR__ . '/gh8461-002.inc';
20+
21+
for ($i = 0; $i < 10; $i++) {
22+
UniqueList::foo();
23+
}
24+
25+
// mark the file as changed (important)
26+
touch(__DIR__ . '/gh8461-002.inc');
27+
28+
print "OK";
29+
--EXPECT--
30+
OK

ext/opcache/tests/jit/gh8461-003.inc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
$x = 0;
4+
5+
class UniqueList
6+
{
7+
public const A = 1;
8+
public const B = 1;
9+
10+
private $foo;
11+
12+
public function __construct($b)
13+
{
14+
global $x;
15+
$x++;
16+
17+
$this->foo = self::A + $b;
18+
}
19+
}

ext/opcache/tests/jit/gh8461-003.phpt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Bug GH-8461 003 (JIT does not account for class re-compile)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.jit_buffer_size=1M
9+
opcache.jit=1255
10+
opcache.file_update_protection=0
11+
opcache.revalidate_freq=0
12+
opcache.protect_memory=1
13+
--FILE--
14+
<?php
15+
16+
// Checks that JITed code does not crash in --repeat 2 after the UniqueList
17+
// class is recompiled.
18+
19+
require __DIR__ . '/gh8461-003.inc';
20+
21+
class UniqueListLast extends UniqueList
22+
{
23+
public function __construct()
24+
{
25+
parent::__construct(self::B);
26+
}
27+
}
28+
29+
for ($i = 0; $i < 10; $i++) {
30+
new UniqueListLast();
31+
}
32+
33+
// mark the file as changed (important)
34+
touch(__DIR__ . '/gh8461-003.inc');
35+
36+
print "OK";
37+
--EXPECT--
38+
OK

ext/opcache/tests/jit/gh8461-004.inc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
$x = 0;
4+
5+
class UniqueList
6+
{
7+
public const A = 1;
8+
public const B = 1;
9+
10+
private $foo;
11+
12+
public function __construct($b)
13+
{
14+
global $x;
15+
$x++;
16+
17+
$this->foo = self::A + $b;
18+
}
19+
}

0 commit comments

Comments
 (0)