From 7b6523c3e5ea1f82ce6b0a2fb3caa8c3d35a2600 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:43:52 +0100 Subject: [PATCH 1/6] 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). --- Zend/Optimizer/zend_func_info.c | 6 +++--- Zend/Optimizer/zend_inference.c | 4 ++-- Zend/Optimizer/zend_inference.h | 29 +++++++++++++++-------------- ext/opcache/jit/zend_jit.c | 17 +++++++++++------ 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index 7ce11c2c8772a..af9d5a872d577 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -59,15 +59,15 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa && !(ssa->cfg.flags & ZEND_SSA_TSSA)) { zend_op_array *op_array = call_info->caller_op_array; uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline, - &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]); + &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes], 0); uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline, - &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]); + &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes], 0); uint32_t t3 = 0; uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY; if (call_info->num_args == 3) { t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline, - &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]); + &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes], 0); } if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) { tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index a4aca39e2edbc..6b929c5d471dd 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -5185,7 +5185,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op return 0; case ZEND_ASSIGN_DIM: if ((opline+1)->op1_type == IS_CV) { - if (_ssa_op1_info(op_array, ssa, opline+1, ssa_op+1) & MAY_BE_UNDEF) { + if (OP1_DATA_INFO() & MAY_BE_UNDEF) { return 1; } } @@ -5196,7 +5196,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op return 1; } if ((opline+1)->op1_type == IS_CV) { - if (_ssa_op1_info(op_array, ssa, opline+1, ssa_op+1) & MAY_BE_UNDEF) { + if (OP1_DATA_INFO() & MAY_BE_UNDEF) { return 1; } } diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index ffcf4f0ae1379..6e3cd4968f65c 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -172,19 +172,20 @@ static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa } #define DEFINE_SSA_OP_INFO(opN) \ - static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ + static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, size_t offset) \ { \ + opline += offset; \ if (opline->opN##_type == IS_CONST) { \ return _const_op_type(CRT_CONSTANT(opline->opN)); \ } else { \ - return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \ + return get_ssa_var_info(ssa, ssa->var_info ? (ssa_op + offset)->opN##_use : -1); \ } \ } \ #define DEFINE_SSA_OP_DEF_INFO(opN) \ - static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ + static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, size_t offset) \ { \ - return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \ + return get_ssa_var_info(ssa, ssa->var_info ? (ssa_op + offset)->opN##_def : -1); \ } \ @@ -195,16 +196,16 @@ DEFINE_SSA_OP_DEF_INFO(op1) DEFINE_SSA_OP_DEF_INFO(op2) DEFINE_SSA_OP_DEF_INFO(result) -#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op)) -#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op)) -#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op)) -#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op)) -#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op)) -#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op)) +#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op, 0)) +#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op, 0)) +#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op, 1)) +#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op, 1)) +#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op, 0)) +#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op, 0)) +#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op, 0)) +#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op, 1)) +#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op, 1)) +#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op, 0)) static zend_always_inline bool zend_add_will_overflow(zend_long a, zend_long b) { return (b > 0 && a > ZEND_LONG_MAX - b) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 294ed1db8eaf7..9c6296c70dc50 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -243,6 +243,11 @@ static int zend_jit_is_constant_cmp_long_long(const zend_op *opline, return 0; } +#define ADVANCE_SSA_OP(ssa_op, offset) \ + do { \ + if (ssa_op) ssa_op += offset; \ + } while (0) + 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) { int skip; @@ -250,7 +255,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons if (trace) { zend_jit_trace_rec *p = trace; - ssa_op++; + ADVANCE_SSA_OP(ssa_op, 1); while (1) { if (p->op == ZEND_JIT_TRACE_VM) { switch (p->opline->opcode) { @@ -305,7 +310,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons return 1; } } - ssa_op += zend_jit_trace_op_len(opline); + ADVANCE_SSA_OP(ssa_op, zend_jit_trace_op_len(opline)); } else if (p->op == ZEND_JIT_TRACE_ENTER || p->op == ZEND_JIT_TRACE_BACK || 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 const zend_op *end = op_array->opcodes + op_array->last; opline++; - ssa_op++; + ADVANCE_SSA_OP(ssa_op, 1); skip = (call_level == 1); while (opline != end) { if (!skip) { @@ -381,7 +386,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons return 0; } opline++; - ssa_op++; + ADVANCE_SSA_OP(ssa_op, 1); } return 1; @@ -395,7 +400,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons } opline++; - ssa_op++; + ADVANCE_SSA_OP(ssa_op, 1); skip = (call_level == 1); while (opline != end) { if (skip) { @@ -421,7 +426,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons } } opline++; - ssa_op++; + ADVANCE_SSA_OP(ssa_op, 1); } return 0; From befd724ce543248f3f7656cb5cec098147d27a11 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 31 Mar 2024 01:25:11 +0100 Subject: [PATCH 2/6] Remove -fno-sanitize=pointer-overflow flag from CI --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 7f014501528c4..ca6945e7c2275 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -122,7 +122,7 @@ jobs: configurationParameters: >- --${{ matrix.debug && 'enable' || 'disable' }}-debug --${{ matrix.zts && 'enable' || 'disable' }}-zts - ${{ 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' || '' }} + ${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address" CC=clang-16 CXX=clang++-16' || '' }} skipSlow: ${{ matrix.asan }} - name: make run: make -j$(/usr/bin/nproc) >/dev/null From 09be586eb859084fe5c3d78566ac199163ab1507 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 31 Mar 2024 14:34:14 +0200 Subject: [PATCH 3/6] Fix NULL pointer offsets added to the stack_map --- ext/opcache/jit/zend_jit_ir.c | 2 +- ext/opcache/jit/zend_jit_trace.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index cb595becb1790..9c07a734e2a42 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -16301,7 +16301,7 @@ static int zend_jit_trace_start(zend_jit_ctx *jit, if (parent) { int i; int parent_vars_count = parent->exit_info[exit_num].stack_size; - zend_jit_trace_stack *parent_stack = + zend_jit_trace_stack *parent_stack = parent_vars_count == 0 ? NULL : parent->stack_map + parent->exit_info[exit_num].stack_offset; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 1bb5f8e8daf6c..749b2a3078121 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2779,7 +2779,7 @@ static zend_jit_reg_var* zend_jit_trace_allocate_registers(zend_jit_trace_rec *t zend_jit_trace_stack *stack; uint32_t parent_vars_count = parent_trace ? zend_jit_traces[parent_trace].exit_info[exit_num].stack_size : 0; - zend_jit_trace_stack *parent_stack = parent_trace ? + zend_jit_trace_stack *parent_stack = parent_vars_count ? zend_jit_traces[parent_trace].stack_map + zend_jit_traces[parent_trace].exit_info[exit_num].stack_offset : NULL; 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 /* Deoptimization of VM stack state */ uint32_t i; uint32_t stack_size = t->exit_info[exit_num].stack_size; - zend_jit_trace_stack *stack = t->stack_map + t->exit_info[exit_num].stack_offset; + zend_jit_trace_stack *stack = stack_size ? t->stack_map + t->exit_info[exit_num].stack_offset : NULL; if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_RESTORE_CALL) { zend_execute_data *call = (zend_execute_data *)regs->gpr[ZREG_RX]; From 1e7a5405b6d7d11fe2e5bdee1955188c61a0c738 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 31 Mar 2024 14:36:04 +0200 Subject: [PATCH 4/6] Fix an offset add on a potentially NULL ssa->ops --- ext/opcache/jit/zend_jit.c | 2 +- ext/opcache/jit/zend_jit_ir.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 9c6296c70dc50..caf9e8d086c6e 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -446,7 +446,7 @@ static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ss if (ZEND_TYPE_IS_SET(arg_info->type)) { if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) { zend_op *opline = call_info->arg_info[num_args].opline; - zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; + zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL; uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type); if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) { break; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 9c07a734e2a42..dfd667599e623 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -15240,7 +15240,7 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_ jit->b = -1; } } else { - zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; + zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL; uint32_t op1_info = OP1_INFO(); zend_jit_addr op1_addr = OP1_ADDR(); const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); From f7c9a5c2b45f4e70e860563b5761596e570ded83 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:19:35 +0200 Subject: [PATCH 5/6] Fix NULL pointer arithmetic in zend_range_info() --- Zend/Optimizer/zend_func_info.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index af9d5a872d577..7e1a388989e74 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -58,16 +58,16 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa && ssa && !(ssa->cfg.flags & ZEND_SSA_TSSA)) { zend_op_array *op_array = call_info->caller_op_array; - uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline, - &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes], 0); - uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline, - &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes], 0); + uint32_t t1 = _ssa_op1_info(op_array, ssa, op_array->opcodes, + ssa->ops, call_info->arg_info[0].opline - op_array->opcodes); + uint32_t t2 = _ssa_op1_info(op_array, ssa, op_array->opcodes, + ssa->ops, call_info->arg_info[1].opline - op_array->opcodes); uint32_t t3 = 0; uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY; if (call_info->num_args == 3) { - t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline, - &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes], 0); + t3 = _ssa_op1_info(op_array, ssa, op_array->opcodes, + ssa->ops, call_info->arg_info[2].opline - op_array->opcodes); } if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) { tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; From a37ee2fe928ceb43bc5b988dc4b3287a71406d76 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:06:36 +0200 Subject: [PATCH 6/6] Address review comments --- Zend/Optimizer/zend_func_info.c | 6 +++--- Zend/Optimizer/zend_inference.h | 29 ++++++++++++++--------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index 7e1a388989e74..964c713c02566 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -59,15 +59,15 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa && !(ssa->cfg.flags & ZEND_SSA_TSSA)) { zend_op_array *op_array = call_info->caller_op_array; uint32_t t1 = _ssa_op1_info(op_array, ssa, op_array->opcodes, - ssa->ops, call_info->arg_info[0].opline - op_array->opcodes); + ssa->ops ? &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes] : NULL); uint32_t t2 = _ssa_op1_info(op_array, ssa, op_array->opcodes, - ssa->ops, call_info->arg_info[1].opline - op_array->opcodes); + ssa->ops ? &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes] : NULL); uint32_t t3 = 0; uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY; if (call_info->num_args == 3) { t3 = _ssa_op1_info(op_array, ssa, op_array->opcodes, - ssa->ops, call_info->arg_info[2].opline - op_array->opcodes); + ssa->ops ? &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes] : NULL); } if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) { tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index 6e3cd4968f65c..666abc586592e 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -172,20 +172,19 @@ static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa } #define DEFINE_SSA_OP_INFO(opN) \ - static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, size_t offset) \ + static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ { \ - opline += offset; \ if (opline->opN##_type == IS_CONST) { \ return _const_op_type(CRT_CONSTANT(opline->opN)); \ } else { \ - return get_ssa_var_info(ssa, ssa->var_info ? (ssa_op + offset)->opN##_use : -1); \ + return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \ } \ } \ #define DEFINE_SSA_OP_DEF_INFO(opN) \ - static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, size_t offset) \ + static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ { \ - return get_ssa_var_info(ssa, ssa->var_info ? (ssa_op + offset)->opN##_def : -1); \ + return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \ } \ @@ -196,16 +195,16 @@ DEFINE_SSA_OP_DEF_INFO(op1) DEFINE_SSA_OP_DEF_INFO(op2) DEFINE_SSA_OP_DEF_INFO(result) -#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op, 0)) -#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op, 0)) -#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op, 1)) -#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op, 1)) -#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op, 0)) -#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op, 0)) -#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op, 0)) -#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op, 1)) -#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op, 1)) -#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op, 0)) +#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op)) +#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op)) +#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL)) +#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL)) +#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op)) +#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op)) +#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op)) +#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL)) +#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL)) +#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op)) static zend_always_inline bool zend_add_will_overflow(zend_long a, zend_long b) { return (b > 0 && a > ZEND_LONG_MAX - b)