]> granicus.if.org Git - php/commitdiff
Fixed JIT for integer overflow checks
authorDmitry Stogov <dmitry@zend.com>
Tue, 26 May 2020 14:59:15 +0000 (17:59 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 26 May 2020 14:59:15 +0000 (17:59 +0300)
ext/opcache/jit/zend_jit.c
ext/opcache/jit/zend_jit_trace.c
ext/opcache/jit/zend_jit_x86.dasc
ext/opcache/jit/zend_jit_x86.h

index a3b3661d74537df6caae2b8bfa7289498acc28c2..450a037f7721390fca7aa8e7467cfd3cbea0ea15 100644 (file)
@@ -2213,7 +2213,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                                op1_def_info, OP1_DEF_REG_ADDR(),
                                                                res_use_info, res_info,
                                                                res_addr,
-                                                               (op1_def_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -2295,7 +2295,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                                op2_info, OP2_REG_ADDR(),
                                                                res_use_info, res_info, res_addr,
                                                                send_result,
-                                                               (res_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -2375,7 +2375,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                if (!zend_jit_assign_op(&dasm_state, opline, op_array,
                                                                op1_info, op1_def_info, OP1_RANGE(),
                                                                op2_info, OP2_RANGE(),
-                                                               (op1_def_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info && MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
index ef8ea3696a580e5c1d6bfe64ce4232a7bb88f384..5906aca44496cd8516ea872160b0685710004a38 100644 (file)
@@ -1866,7 +1866,9 @@ static zend_lifetime_interval** zend_jit_trace_allocate_registers(zend_jit_trace
                if ((ssa->vars[i].use_chain >= 0 /*|| ssa->vars[i].phi_use_chain*/)
                 && zend_jit_var_supports_reg(ssa, i)) {
                        start[i] = 0;
-                       if (i < parent_vars_count && STACK_REG(parent_stack, i) != ZREG_NONE) {
+                       if (i < parent_vars_count
+                        && STACK_REG(parent_stack, i) != ZREG_NONE
+                        && STACK_REG(parent_stack, i) < ZREG_NUM) {
                                /* We will try to reuse register from parent trace */
                                count += 2;
                        } else {
@@ -2197,7 +2199,9 @@ static zend_lifetime_interval** zend_jit_trace_allocate_registers(zend_jit_trace
                }
                while (i > 0) {
                        i--;
-                       if (intervals[i] && STACK_REG(parent_stack, i) != ZREG_NONE) {
+                       if (intervals[i]
+                        && STACK_REG(parent_stack, i) != ZREG_NONE
+                        && STACK_REG(parent_stack, i) < ZREG_NUM) {
                                list[j].ssa_var = - 1;
                                list[j].reg = STACK_REG(parent_stack, i);
                                list[j].flags = 0;
@@ -2602,8 +2606,15 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                        if (ra && ra[i] && ra[i]->reg == STACK_REG(parent_stack, i)) {
                                            /* register already loaded by parent trace */
                                                SET_STACK_REG(stack, i, ra[i]->reg);
-                                       } else if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) {
-                                               goto jit_failure;
+                                       } else if (STACK_REG(parent_stack, i) < ZREG_NUM) {
+                                               if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) {
+                                                       goto jit_failure;
+                                               }
+                                       } else {
+                                               SET_STACK_REG(stack, i, ZREG_NONE);
+                                               if (!zend_jit_store_const(&dasm_state, i, STACK_REG(parent_stack, i))) {
+                                                       goto jit_failure;
+                                               }
                                        }
                                }
                        }
@@ -2760,7 +2771,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                                op1_def_info, OP1_DEF_REG_ADDR(),
                                                                res_use_info, res_info,
                                                                res_addr,
-                                                               (op1_def_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+                                                               (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -2882,7 +2893,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                                op2_info, OP2_REG_ADDR(),
                                                                res_use_info, res_info, res_addr,
                                                                send_result,
-                                                               (res_info & MAY_BE_LONG) && (res_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -2979,7 +2990,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                if (!zend_jit_assign_op(&dasm_state, opline, op_array,
                                                                op1_info, op1_def_info, OP1_RANGE(),
                                                                op2_info, OP2_RANGE(),
-                                                               (op1_def_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -4058,8 +4069,14 @@ done:
                        for (i = 0; i < op_array->last_var + op_array->T; i++) {
                                if (STACK_REG(stack, i) != ZREG_NONE) {
                                        // TODO: optimize out useless stores ????
-                                       if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
-                                               goto jit_failure;
+                                       if (STACK_REG(stack, i) < ZREG_NUM) {
+                                               if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
+                                                       goto jit_failure;
+                                               }
+                                       } else {
+                                               if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) {
+                                                       goto jit_failure;
+                                               }
                                        }
                                }
                        }
@@ -4183,8 +4200,14 @@ static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_n
        stack = zend_jit_traces[trace_num].stack_map + zend_jit_traces[trace_num].exit_info[exit_num].stack_offset;
        for (i = 0; i < stack_size; i++) {
                if (STACK_REG(stack, i) != ZREG_NONE) {
-                       if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
-                               goto jit_failure;
+                       if (STACK_REG(stack, i) < ZREG_NUM) {
+                               if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
+                                       goto jit_failure;
+                               }
+                       } else {
+                               if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) {
+                                       goto jit_failure;
+                               }
                        }
                }
        }
@@ -4595,7 +4618,11 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
                                } else {
                                        fprintf(stderr, "%s", zend_get_type_by_const(type));
                                        if (STACK_REG(stack, j) != ZREG_NONE) {
-                                               fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
+                                               if (STACK_REG(stack, j) < ZREG_NUM) {
+                                                       fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
+                                               } else {
+                                                       fprintf(stderr, "(const_%d)", STACK_REG(stack, j) - ZREG_NUM);
+                                               }
                                        }
                                }
                        }
@@ -5014,9 +5041,31 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
        for (i = 0; i < stack_size; i++) {
                if (STACK_REG(stack, i) != ZREG_NONE) {
                        if (STACK_TYPE(stack, i) == IS_LONG) {
-                               ZVAL_LONG(EX_VAR_NUM(i), regs->r[STACK_REG(stack, i)]);
+                               zend_long val;
+
+                               if (STACK_REG(stack, i) < ZREG_NUM) {
+                                       val = regs->r[STACK_REG(stack, i)];
+                               } else if (STACK_REG(stack, i) == ZREG_LONG_MIN) {
+                                       val = ZEND_LONG_MIN;
+                               } else if (STACK_REG(stack, i) == ZREG_LONG_MAX) {
+                                       val = ZEND_LONG_MAX;
+                               } else {
+                                       ZEND_ASSERT(0);
+                               }
+                               ZVAL_LONG(EX_VAR_NUM(i), val);
                        } else if (STACK_TYPE(stack, i) == IS_DOUBLE) {
-                               ZVAL_DOUBLE(EX_VAR_NUM(i), regs->xmm[STACK_REG(stack, i) - ZREG_XMM0]);
+                               double val;
+
+                               if (STACK_REG(stack, i) < ZREG_NUM) {
+                                       val = regs->xmm[STACK_REG(stack, i) - ZREG_XMM0];
+                               } else if (STACK_REG(stack, i) == ZREG_LONG_MIN_MINUS_1) {
+                                       val = (double)ZEND_LONG_MIN - 1.0;
+                               } else if (STACK_REG(stack, i) == ZREG_LONG_MAX_PLUS_1) {
+                                       val = (double)ZEND_LONG_MAX + 1.0;
+                               } else {
+                                       ZEND_ASSERT(0);
+                               }
+                               ZVAL_DOUBLE(EX_VAR_NUM(i), val);
                        } else {
                                ZEND_ASSERT(0);
                        }
index 6bd05b05e41bbacc5d81f032298ad62727a73c13..cf0d74646a65fdd5a865de81db9d7e3ee03b3d47 100644 (file)
@@ -3511,6 +3511,50 @@ static int zend_jit_update_regs(dasm_State **Dst, zend_jit_addr src, zend_jit_ad
        return 1;
 }
 
+static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
+{
+       zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
+
+       if (reg == ZREG_LONG_MIN_MINUS_1) {
+               |.if X64
+                       |       SET_ZVAL_LVAL dst, 0x00000000
+                       |       SET_ZVAL_W2 dst, 0xc3e00000
+               |.else
+                       |       SET_ZVAL_LVAL dst, 0x00200000
+                       |       SET_ZVAL_W2 dst, 0xc1e00000
+               |.endif
+               |       SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
+       } else if (reg == ZREG_LONG_MIN) {
+               |.if X64
+                       |       SET_ZVAL_LVAL dst, 0x00000000
+                       |       SET_ZVAL_W2 dst, 0x80000000
+               |.else
+                       |       SET_ZVAL_LVAL dst, ZEND_LONG_MIN
+               |.endif
+               |       SET_ZVAL_TYPE_INFO dst, IS_LONG
+       } else if (reg == ZREG_LONG_MAX) {
+               |.if X64
+                       |       SET_ZVAL_LVAL dst, 0xffffffff
+                       |       SET_ZVAL_W2 dst, 0x7fffffff
+               |.else
+                       |       SET_ZVAL_LVAL dst, ZEND_LONG_MAX
+               |.endif
+               |       SET_ZVAL_TYPE_INFO dst, IS_LONG
+       } else if (reg == ZREG_LONG_MAX_PLUS_1) {
+               |.if X64
+                       |       SET_ZVAL_LVAL dst, 0
+                       |       SET_ZVAL_W2 dst, 0x43e00000
+               |.else
+                       |       SET_ZVAL_LVAL dst, 0
+                       |       SET_ZVAL_W2 dst, 0x41e00000
+               |.endif
+               |       SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
+       } else {
+               ZEND_ASSERT(0);
+       }
+       return 1;
+}
+
 static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op1_def_info, zend_jit_addr op1_def_addr, uint32_t res_use_info, uint32_t res_info, zend_jit_addr res_addr, int may_overflow, int may_throw)
 {
        if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_LONG)) {
@@ -3528,14 +3572,52 @@ static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, const zend_
                |       LONG_OP_WITH_CONST sub, op1_def_addr, Z_L(1)
        }
 
-       if (may_overflow && ((op1_def_info & MAY_BE_GUARD) || (opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD)))) {
-               int32_t exit_point = zend_jit_trace_get_exit_point(opline, opline, NULL, 0);
-               const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+       if (may_overflow &&
+           (((op1_def_info & MAY_BE_GUARD) && (op1_def_info & MAY_BE_LONG)) ||
+            ((opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD) && (res_info & MAY_BE_LONG))))) {
+               int32_t exit_point;
+               const void *exit_addr;
+               zend_jit_trace_stack *stack;
+               uint32_t old_op1_info, old_res_info;
+
+               stack = JIT_G(current_frame)->stack;
+               old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
+               SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_DOUBLE);
+               if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
+                       SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MAX_PLUS_1);
+               } else {
+                       SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MIN_MINUS_1);
+               }
+               if (opline->result_type != IS_UNUSED) {
+                       old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+                       if (opline->opcode == ZEND_PRE_INC) {
+                               SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE);
+                               SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX_PLUS_1);
+                       } else if (opline->opcode == ZEND_PRE_DEC) {
+                               SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE);
+                               SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN_MINUS_1);
+                       } else if (opline->opcode == ZEND_POST_INC) {
+                               SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG);
+                               SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX);
+                       } else if (opline->opcode == ZEND_POST_DEC) {
+                               SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG);
+                               SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN);
+                       }
+               }
+
+               exit_point = zend_jit_trace_get_exit_point(opline, opline + 1, NULL, 0);
+               exit_addr = zend_jit_trace_get_exit_addr(exit_point);
                |       jo &exit_addr
+
                if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
                    opline->result_type != IS_UNUSED) {
                        |       ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
                }
+
+               SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
+               if (opline->result_type != IS_UNUSED) {
+                       SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
+               }
        } else if (may_overflow) {
                |       jo >1
                if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
@@ -3561,7 +3643,9 @@ static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, const zend_
                                |       SET_ZVAL_W2 op1_def_addr, 0xc1e00000
                        |.endif
                }
-               |       SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE
+               if (Z_MODE(op1_def_addr) == IS_MEM_ZVAL) {
+                       |       SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE
+               }
                if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
                    opline->result_type != IS_UNUSED) {
                        |       ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_DOUBLE, ZREG_R0, ZREG_R1
index 5bf2521815c6c953a0b7e71b0c7e8324b9867a5e..3368820d446e9b90ce7ab961dffef60ff0d4da93 100644 (file)
@@ -62,7 +62,13 @@ typedef enum _zend_reg {
        ZREG_XMM15,
 #endif
 
-       ZREG_NUM
+       ZREG_NUM,
+
+       /* pseudo constants used by deoptimizer */
+       ZREG_LONG_MIN_MINUS_1,
+       ZREG_LONG_MIN,
+       ZREG_LONG_MAX,
+       ZREG_LONG_MAX_PLUS_1,
 } zend_reg;
 
 typedef struct _zend_jit_registers_buf {