Skip to content

Commit 00c6d53

Browse files
authored
Fix GH-13834: Applying non-zero offset 36 to null pointer in zend_jit.c (#13846)
* Fix GH-13834: Applying non-zero offset 36 to null pointer in zend_jit.c ssa_op can be NULL in function JIT. Doing pointer arithmetic on a NULL pointer is undefined behaviour. Undefined behaviour can be dangerous because the optimizer may assume then that the variable is not actually NULL. To solve this: 1. Add ADVANCE_SSA_OP() to safely add an offset to ssa_op in zend_jit.c 2. For inference, add an extra offset argument to the helper functions. To reproduce this, use Clang (not GCC) on a test like sapi/cli/tests/gh12363.phpt (or other tests also work). * Remove -fno-sanitize=pointer-overflow flag from CI * Fix NULL pointer offsets added to the stack_map * Fix an offset add on a potentially NULL ssa->ops * Fix NULL pointer arithmetic in zend_range_info() * Address review comments
1 parent c3f5bbd commit 00c6d53

File tree

7 files changed

+29
-24
lines changed

7 files changed

+29
-24
lines changed

.github/workflows/push.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ jobs:
122122
configurationParameters: >-
123123
--${{ matrix.debug && 'enable' || 'disable' }}-debug
124124
--${{ matrix.zts && 'enable' || 'disable' }}-zts
125-
${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -fno-sanitize=pointer-overflow -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address -fno-sanitize=pointer-overflow" CC=clang-16 CXX=clang++-16' || '' }}
125+
${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address" CC=clang-16 CXX=clang++-16' || '' }}
126126
skipSlow: ${{ matrix.asan }}
127127
- name: make
128128
run: make -j$(/usr/bin/nproc) >/dev/null

Zend/Optimizer/zend_func_info.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa
5858
&& ssa
5959
&& !(ssa->cfg.flags & ZEND_SSA_TSSA)) {
6060
zend_op_array *op_array = call_info->caller_op_array;
61-
uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline,
62-
&ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]);
63-
uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
64-
&ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
61+
uint32_t t1 = _ssa_op1_info(op_array, ssa, op_array->opcodes,
62+
ssa->ops ? &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes] : NULL);
63+
uint32_t t2 = _ssa_op1_info(op_array, ssa, op_array->opcodes,
64+
ssa->ops ? &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes] : NULL);
6565
uint32_t t3 = 0;
6666
uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY;
6767

6868
if (call_info->num_args == 3) {
69-
t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline,
70-
&ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]);
69+
t3 = _ssa_op1_info(op_array, ssa, op_array->opcodes,
70+
ssa->ops ? &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes] : NULL);
7171
}
7272
if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
7373
tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;

Zend/Optimizer/zend_inference.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5185,7 +5185,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
51855185
return 0;
51865186
case ZEND_ASSIGN_DIM:
51875187
if ((opline+1)->op1_type == IS_CV) {
5188-
if (_ssa_op1_info(op_array, ssa, opline+1, ssa_op+1) & MAY_BE_UNDEF) {
5188+
if (OP1_DATA_INFO() & MAY_BE_UNDEF) {
51895189
return 1;
51905190
}
51915191
}
@@ -5196,7 +5196,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
51965196
return 1;
51975197
}
51985198
if ((opline+1)->op1_type == IS_CV) {
5199-
if (_ssa_op1_info(op_array, ssa, opline+1, ssa_op+1) & MAY_BE_UNDEF) {
5199+
if (OP1_DATA_INFO() & MAY_BE_UNDEF) {
52005200
return 1;
52015201
}
52025202
}

Zend/Optimizer/zend_inference.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,13 @@ DEFINE_SSA_OP_DEF_INFO(result)
197197

198198
#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op))
199199
#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op))
200-
#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
201-
#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
200+
#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL))
201+
#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL))
202202
#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op))
203203
#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op))
204204
#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op))
205-
#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
206-
#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
205+
#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL))
206+
#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL))
207207
#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op))
208208

209209
static zend_always_inline bool zend_add_will_overflow(zend_long a, zend_long b) {

ext/opcache/jit/zend_jit.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,19 @@ static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
243243
return 0;
244244
}
245245

246+
#define ADVANCE_SSA_OP(ssa_op, offset) \
247+
do { \
248+
if (ssa_op) ssa_op += offset; \
249+
} while (0)
250+
246251
static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, const zend_op *opline, int call_level, zend_jit_trace_rec *trace)
247252
{
248253
int skip;
249254

250255
if (trace) {
251256
zend_jit_trace_rec *p = trace;
252257

253-
ssa_op++;
258+
ADVANCE_SSA_OP(ssa_op, 1);
254259
while (1) {
255260
if (p->op == ZEND_JIT_TRACE_VM) {
256261
switch (p->opline->opcode) {
@@ -305,7 +310,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
305310
return 1;
306311
}
307312
}
308-
ssa_op += zend_jit_trace_op_len(opline);
313+
ADVANCE_SSA_OP(ssa_op, zend_jit_trace_op_len(opline));
309314
} else if (p->op == ZEND_JIT_TRACE_ENTER ||
310315
p->op == ZEND_JIT_TRACE_BACK ||
311316
p->op == ZEND_JIT_TRACE_END) {
@@ -319,7 +324,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
319324
const zend_op *end = op_array->opcodes + op_array->last;
320325

321326
opline++;
322-
ssa_op++;
327+
ADVANCE_SSA_OP(ssa_op, 1);
323328
skip = (call_level == 1);
324329
while (opline != end) {
325330
if (!skip) {
@@ -381,7 +386,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
381386
return 0;
382387
}
383388
opline++;
384-
ssa_op++;
389+
ADVANCE_SSA_OP(ssa_op, 1);
385390
}
386391

387392
return 1;
@@ -395,7 +400,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
395400
}
396401

397402
opline++;
398-
ssa_op++;
403+
ADVANCE_SSA_OP(ssa_op, 1);
399404
skip = (call_level == 1);
400405
while (opline != end) {
401406
if (skip) {
@@ -421,7 +426,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
421426
}
422427
}
423428
opline++;
424-
ssa_op++;
429+
ADVANCE_SSA_OP(ssa_op, 1);
425430
}
426431

427432
return 0;
@@ -441,7 +446,7 @@ static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ss
441446
if (ZEND_TYPE_IS_SET(arg_info->type)) {
442447
if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) {
443448
zend_op *opline = call_info->arg_info[num_args].opline;
444-
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
449+
zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL;
445450
uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
446451
if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) {
447452
break;

ext/opcache/jit/zend_jit_ir.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15241,7 +15241,7 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_
1524115241
jit->b = -1;
1524215242
}
1524315243
} else {
15244-
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
15244+
zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL;
1524515245
uint32_t op1_info = OP1_INFO();
1524615246
zend_jit_addr op1_addr = OP1_ADDR();
1524715247
const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
@@ -16302,7 +16302,7 @@ static int zend_jit_trace_start(zend_jit_ctx *jit,
1630216302
if (parent) {
1630316303
int i;
1630416304
int parent_vars_count = parent->exit_info[exit_num].stack_size;
16305-
zend_jit_trace_stack *parent_stack =
16305+
zend_jit_trace_stack *parent_stack = parent_vars_count == 0 ? NULL :
1630616306
parent->stack_map +
1630716307
parent->exit_info[exit_num].stack_offset;
1630816308

ext/opcache/jit/zend_jit_trace.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,7 +2779,7 @@ static zend_jit_reg_var* zend_jit_trace_allocate_registers(zend_jit_trace_rec *t
27792779
zend_jit_trace_stack *stack;
27802780
uint32_t parent_vars_count = parent_trace ?
27812781
zend_jit_traces[parent_trace].exit_info[exit_num].stack_size : 0;
2782-
zend_jit_trace_stack *parent_stack = parent_trace ?
2782+
zend_jit_trace_stack *parent_stack = parent_vars_count ?
27832783
zend_jit_traces[parent_trace].stack_map +
27842784
zend_jit_traces[parent_trace].exit_info[exit_num].stack_offset : NULL;
27852785
checkpoint = zend_arena_checkpoint(CG(arena));
@@ -8386,7 +8386,7 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
83868386
/* Deoptimization of VM stack state */
83878387
uint32_t i;
83888388
uint32_t stack_size = t->exit_info[exit_num].stack_size;
8389-
zend_jit_trace_stack *stack = t->stack_map + t->exit_info[exit_num].stack_offset;
8389+
zend_jit_trace_stack *stack = stack_size ? t->stack_map + t->exit_info[exit_num].stack_offset : NULL;
83908390

83918391
if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
83928392
zend_execute_data *call = (zend_execute_data *)regs->gpr[ZREG_RX];

0 commit comments

Comments
 (0)