Skip to content

Commit 668ac03

Browse files
committed
Merge branch 'PHP-8.0'
* PHP-8.0: Move stack overflow checks out of the loops
2 parents ca5c8c3 + 98e4f94 commit 668ac03

File tree

4 files changed

+131
-49
lines changed

4 files changed

+131
-49
lines changed

ext/opcache/jit/zend_jit.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2723,7 +2723,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
27232723
case ZEND_INIT_FCALL:
27242724
case ZEND_INIT_FCALL_BY_NAME:
27252725
case ZEND_INIT_NS_FCALL_BY_NAME:
2726-
if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level, NULL)) {
2726+
if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level, NULL, 1)) {
27272727
goto jit_failure;
27282728
}
27292729
goto done;
@@ -3296,7 +3296,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
32963296
}
32973297
if (!zend_jit_init_method_call(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level,
32983298
op1_info, op1_addr, ce, ce_is_instanceof, 0, NULL,
3299-
NULL)) {
3299+
NULL, 1)) {
33003300
goto jit_failure;
33013301
}
33023302
goto done;

ext/opcache/jit/zend_jit_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ struct _zend_jit_trace_stack_frame {
392392
const zend_op *call_opline;
393393
uint32_t call_level;
394394
uint32_t _info;
395+
int used_stack;
395396
zend_jit_trace_stack stack[1];
396397
};
397398

ext/opcache/jit/zend_jit_trace.c

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,7 @@ static int is_checked_guard(const zend_ssa *tssa, const zend_op **ssa_opcodes, u
987987
typedef struct _zend_tssa {
988988
zend_ssa ssa;
989989
const zend_op **tssa_opcodes;
990+
int used_stack;
990991
} zend_tssa;
991992

992993
static const zend_op _nop_opcode = {0};
@@ -1005,7 +1006,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
10051006
zend_jit_trace_stack *stack;
10061007
uint32_t build_flags = ZEND_SSA_RC_INFERENCE | ZEND_SSA_USE_CV_RESULTS;
10071008
uint32_t optimization_level = 0;
1008-
int call_level, level, num_op_arrays;
1009+
int call_level, level, num_op_arrays, used_stack, max_used_stack;
10091010
size_t frame_size, stack_top, stack_size, stack_bottom;
10101011
zend_jit_op_array_trace_extension *jit_extension;
10111012
zend_ssa *ssa;
@@ -1172,6 +1173,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
11721173
tssa->cfg.blocks[0].successors_count = 0;
11731174
tssa->cfg.blocks[0].predecessors_count = 0;
11741175
}
1176+
((zend_tssa*)tssa)->used_stack = -1;
11751177

11761178
if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
11771179
return tssa;
@@ -1440,11 +1442,18 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
14401442
top = zend_jit_trace_call_frame(frame, op_array);
14411443
TRACE_FRAME_INIT(frame, op_array, 0, 0);
14421444
TRACE_FRAME_SET_RETURN_SSA_VAR(frame, -1);
1445+
frame->used_stack = 0;
14431446
for (i = 0; i < op_array->last_var + op_array->T; i++) {
14441447
SET_STACK_TYPE(frame->stack, i, IS_UNKNOWN, 1);
14451448
}
14461449
memset(&return_value_info, 0, sizeof(return_value_info));
14471450

1451+
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
1452+
max_used_stack = used_stack = 0;
1453+
} else {
1454+
max_used_stack = used_stack = -1;
1455+
}
1456+
14481457
p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
14491458
idx = 0;
14501459
level = 0;
@@ -1817,6 +1826,11 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
18171826
ADD_OP2_TRACE_GUARD();
18181827
}
18191828
break;
1829+
case ZEND_SEND_ARRAY:
1830+
case ZEND_SEND_UNPACK:
1831+
case ZEND_CHECK_UNDEF_ARGS:
1832+
case ZEND_INCLUDE_OR_EVAL:
1833+
max_used_stack = used_stack = -1;
18201834
default:
18211835
break;
18221836
}
@@ -1961,6 +1975,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
19611975
/* Trace missed INIT_FCALL opcode */
19621976
call = top;
19631977
TRACE_FRAME_INIT(call, op_array, 0, 0);
1978+
call->used_stack = 0;
19641979
top = zend_jit_trace_call_frame(top, op_array);
19651980
for (i = 0; i < op_array->last_var + op_array->T; i++) {
19661981
SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
@@ -2077,12 +2092,17 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
20772092

20782093
top = frame;
20792094
if (frame->prev) {
2095+
if (used_stack > 0) {
2096+
used_stack -= frame->used_stack;
2097+
}
20802098
frame = frame->prev;
20812099
ZEND_ASSERT(&frame->func->op_array == op_array);
20822100
} else {
2101+
max_used_stack = used_stack = -1;
20832102
frame = zend_jit_trace_ret_frame(frame, op_array);
20842103
TRACE_FRAME_INIT(frame, op_array, 0, 0);
20852104
TRACE_FRAME_SET_RETURN_SSA_VAR(frame, -1);
2105+
frame->used_stack = 0;
20862106
for (i = 0; i < op_array->last_var + op_array->T; i++) {
20872107
SET_STACK_TYPE(frame->stack, i, IS_UNKNOWN, 1);
20882108
}
@@ -2092,13 +2112,41 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
20922112
call = top;
20932113
TRACE_FRAME_INIT(call, p->func, 0, 0);
20942114
call->prev = frame->call;
2115+
call->used_stack = 0;
20952116
frame->call = call;
20962117
top = zend_jit_trace_call_frame(top, p->op_array);
20972118
if (p->func && p->func->type == ZEND_USER_FUNCTION) {
20982119
for (i = 0; i < p->op_array->last_var + p->op_array->T; i++) {
20992120
SET_STACK_INFO(call->stack, i, -1);
21002121
}
21012122
}
2123+
if (used_stack >= 0
2124+
&& !(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
2125+
if (p->func == NULL || (p-1)->op != ZEND_JIT_TRACE_VM) {
2126+
max_used_stack = used_stack = -1;
2127+
} else {
2128+
const zend_op *opline = (p-1)->opline;
2129+
2130+
switch (opline->opcode) {
2131+
case ZEND_INIT_FCALL:
2132+
case ZEND_INIT_FCALL_BY_NAME:
2133+
case ZEND_INIT_NS_FCALL_BY_NAME:
2134+
case ZEND_INIT_METHOD_CALL:
2135+
case ZEND_INIT_DYNAMIC_CALL:
2136+
//case ZEND_INIT_STATIC_METHOD_CALL:
2137+
//case ZEND_INIT_USER_CALL:
2138+
//case ZEND_NEW:
2139+
frame->used_stack = zend_vm_calc_used_stack(opline->extended_value, (zend_function*)p->func);
2140+
used_stack += frame->used_stack;
2141+
if (used_stack > max_used_stack) {
2142+
max_used_stack = used_stack;
2143+
}
2144+
break;
2145+
default:
2146+
max_used_stack = used_stack = -1;
2147+
}
2148+
}
2149+
}
21022150
} else if (p->op == ZEND_JIT_TRACE_DO_ICALL) {
21032151
call = frame->call;
21042152
if (call) {
@@ -2110,6 +2158,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
21102158
}
21112159
}
21122160

2161+
((zend_tssa*)tssa)->used_stack = max_used_stack;
2162+
21132163
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
21142164
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
21152165
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
@@ -3379,6 +3429,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
33793429
zend_uchar res_type = IS_UNKNOWN;
33803430
const zend_op *opline, *orig_opline;
33813431
const zend_ssa_op *ssa_op, *orig_ssa_op;
3432+
int used_stack;
33823433

33833434
JIT_G(current_trace) = trace_buffer;
33843435

@@ -3391,6 +3442,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
33913442
}
33923443

33933444
ssa_opcodes = ((zend_tssa*)ssa)->tssa_opcodes;
3445+
used_stack = ((zend_tssa*)ssa)->used_stack;
33943446

33953447
/* Register allocation */
33963448
if ((JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL))
@@ -3446,6 +3498,12 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
34463498
int parent_vars_count = 0;
34473499
zend_jit_trace_stack *parent_stack = NULL;
34483500

3501+
if (used_stack > 0) {
3502+
if (!zend_jit_stack_check(&dasm_state, opline, used_stack)) {
3503+
goto jit_failure;
3504+
}
3505+
}
3506+
34493507
if (parent_trace) {
34503508
parent_vars_count = MIN(zend_jit_traces[parent_trace].exit_info[exit_num].stack_size,
34513509
op_array->last_var + op_array->T);
@@ -4424,7 +4482,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
44244482
case ZEND_INIT_FCALL:
44254483
case ZEND_INIT_FCALL_BY_NAME:
44264484
case ZEND_INIT_NS_FCALL_BY_NAME:
4427-
if (!zend_jit_init_fcall(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1)) {
4485+
if (!zend_jit_init_fcall(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1, used_stack < 0)) {
44284486
goto jit_failure;
44294487
}
44304488
goto done;
@@ -5403,7 +5461,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
54035461
op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1,
54045462
op_array, ssa, ssa_op, frame->call_level,
54055463
op1_info, op1_addr, ce, ce_is_instanceof, delayed_fetch_this, op1_ce,
5406-
p + 1)) {
5464+
p + 1, used_stack < 0)) {
54075465
goto jit_failure;
54085466
}
54095467
goto done;
@@ -5413,7 +5471,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
54135471
}
54145472
op2_info = OP2_INFO();
54155473
CHECK_OP2_TRACE_TYPE();
5416-
if (!zend_jit_init_closure_call(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1)) {
5474+
if (!zend_jit_init_closure_call(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1, used_stack < 0)) {
54175475
goto jit_failure;
54185476
}
54195477
goto done;

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 66 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8604,7 +8604,25 @@ typedef struct _zend_closure {
86048604
zif_handler orig_internal_handler;
86058605
} zend_closure;
86068606

8607-
static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, zend_function *func, zend_bool is_closure, zend_bool use_this)
8607+
static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_t used_stack)
8608+
{
8609+
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
8610+
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8611+
8612+
if (!exit_addr) {
8613+
return 0;
8614+
}
8615+
8616+
| // Check Stack Overflow
8617+
| MEM_OP2_2_ZTS mov, r1, aword, executor_globals, vm_stack_end, r0
8618+
| MEM_OP2_2_ZTS sub, r1, aword, executor_globals, vm_stack_top, r0
8619+
| cmp r1, used_stack
8620+
| jb &exit_addr
8621+
8622+
return 1;
8623+
}
8624+
8625+
static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, zend_function *func, zend_bool is_closure, zend_bool use_this, zend_bool stack_check)
86088626
{
86098627
uint32_t used_stack;
86108628

@@ -8646,51 +8664,54 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, zen
86468664

86478665
| // if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
86488666
| MEM_OP2_2_ZTS mov, RX, aword, executor_globals, vm_stack_top, RX
8649-
| // Check Stack Overflow
8650-
| MEM_OP2_2_ZTS mov, r2, aword, executor_globals, vm_stack_end, r2
8651-
| sub r2, RX
8652-
if (func) {
8653-
| cmp r2, used_stack
8654-
} else {
8655-
| cmp r2, FCARG1a
8656-
}
8657-
8658-
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8659-
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
8660-
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8661-
8662-
if (!exit_addr) {
8663-
return 0;
8664-
}
86658667

8666-
| jb &exit_addr
8667-
} else {
8668-
| jb >1
8669-
| // EG(vm_stack_top) = (zval*)((char*)call + used_stack);
8670-
|.cold_code
8671-
|1:
8668+
if (stack_check) {
8669+
| // Check Stack Overflow
8670+
| MEM_OP2_2_ZTS mov, r2, aword, executor_globals, vm_stack_end, r2
8671+
| sub r2, RX
86728672
if (func) {
8673-
| mov FCARG1d, used_stack
8673+
| cmp r2, used_stack
8674+
} else {
8675+
| cmp r2, FCARG1a
86748676
}
8677+
8678+
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8679+
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
8680+
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8681+
8682+
if (!exit_addr) {
8683+
return 0;
8684+
}
8685+
8686+
| jb &exit_addr
8687+
} else {
8688+
| jb >1
8689+
| // EG(vm_stack_top) = (zval*)((char*)call + used_stack);
8690+
|.cold_code
8691+
|1:
8692+
if (func) {
8693+
| mov FCARG1d, used_stack
8694+
}
86758695
#ifdef _WIN32
8676-
if (0) {
8696+
if (0) {
86778697
#else
8678-
if (func && func->type == ZEND_INTERNAL_FUNCTION) {
8698+
if (func && func->type == ZEND_INTERNAL_FUNCTION) {
86798699
#endif
8680-
| SET_EX_OPLINE opline, r0
8681-
| EXT_CALL zend_jit_int_extend_stack_helper, r0
8682-
} else {
8683-
if (!is_closure) {
8684-
| mov FCARG2a, r0
8700+
| SET_EX_OPLINE opline, r0
8701+
| EXT_CALL zend_jit_int_extend_stack_helper, r0
86858702
} else {
8686-
| lea FCARG2a, aword [r0 + offsetof(zend_closure, func)]
8703+
if (!is_closure) {
8704+
| mov FCARG2a, r0
8705+
} else {
8706+
| lea FCARG2a, aword [r0 + offsetof(zend_closure, func)]
8707+
}
8708+
| SET_EX_OPLINE opline, r0
8709+
| EXT_CALL zend_jit_extend_stack_helper, r0
86878710
}
8688-
| SET_EX_OPLINE opline, r0
8689-
| EXT_CALL zend_jit_extend_stack_helper, r0
8711+
| mov RX, r0
8712+
| jmp >1
8713+
|.code
86908714
}
8691-
| mov RX, r0
8692-
| jmp >1
8693-
|.code
86948715
}
86958716

86968717
if (func) {
@@ -9032,7 +9053,7 @@ static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zen
90329053
return 1;
90339054
}
90349055

9035-
static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace)
9056+
static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace, zend_bool stack_check)
90369057
{
90379058
zend_func_info *info = ZEND_FUNC_INFO(op_array);
90389059
zend_call_info *call_info = NULL;
@@ -9165,7 +9186,7 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t
91659186
|3:
91669187
}
91679188

9168-
if (!zend_jit_push_call_frame(Dst, opline, func, 0, 0)) {
9189+
if (!zend_jit_push_call_frame(Dst, opline, func, 0, 0, stack_check)) {
91699190
return 0;
91709191
}
91719192

@@ -9194,7 +9215,8 @@ static int zend_jit_init_method_call(dasm_State **Dst,
91949215
zend_bool ce_is_instanceof,
91959216
zend_bool use_this,
91969217
zend_class_entry *trace_ce,
9197-
zend_jit_trace_rec *trace)
9218+
zend_jit_trace_rec *trace,
9219+
zend_bool stack_check)
91989220
{
91999221
zend_func_info *info = ZEND_FUNC_INFO(op_array);
92009222
zend_call_info *call_info = NULL;
@@ -9404,7 +9426,7 @@ static int zend_jit_init_method_call(dasm_State **Dst,
94049426
}
94059427

94069428
if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) == 0) {
9407-
if (!zend_jit_push_call_frame(Dst, opline, func, 0, use_this)) {
9429+
if (!zend_jit_push_call_frame(Dst, opline, func, 0, use_this, stack_check)) {
94089430
return 0;
94099431
}
94109432
}
@@ -9433,7 +9455,8 @@ static int zend_jit_init_closure_call(dasm_State **Dst,
94339455
zend_ssa *ssa,
94349456
const zend_ssa_op *ssa_op,
94359457
int call_level,
9436-
zend_jit_trace_rec *trace)
9458+
zend_jit_trace_rec *trace,
9459+
zend_bool stack_check)
94379460
{
94389461
zend_function *func = NULL;
94399462
zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
@@ -9502,7 +9525,7 @@ static int zend_jit_init_closure_call(dasm_State **Dst,
95029525
}
95039526
}
95049527

9505-
if (!zend_jit_push_call_frame(Dst, opline, func, 1, 0)) {
9528+
if (!zend_jit_push_call_frame(Dst, opline, func, 1, 0, stack_check)) {
95069529
return 0;
95079530
}
95089531

0 commit comments

Comments
 (0)