From bb940d9969e08853d92a09f7a02adc3228cf1c2c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 19 Jun 2019 11:58:42 +0200 Subject: [PATCH] Avoid UB in overflow checks Some of the overflow checks in zend_may_overflow were optimized away by clang, causing JIT failures on release macos. --- ext/opcache/Optimizer/zend_inference.c | 20 +++++++------ ext/opcache/Optimizer/zend_inference.h | 8 ++++++ ext/opcache/Optimizer/zend_ssa.c | 15 ++-------- ext/opcache/jit/zend_jit.c | 40 +++++++++++--------------- 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 62a509dc6d..a6475390a6 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -575,19 +575,21 @@ static int zend_inference_calc_binary_op_range( op2_min = OP2_MIN_RANGE(); op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - tmp->min = op1_min + op2_min; - tmp->max = op1_max + op2_max; if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_UNDERFLOW() || - (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) { + zend_add_will_overflow(op1_min, op2_min)) { tmp->underflow = 1; tmp->min = ZEND_LONG_MIN; + } else { + tmp->min = op1_min + op2_min; } if (OP1_RANGE_OVERFLOW() || OP2_RANGE_OVERFLOW() || - (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) { + zend_add_will_overflow(op1_max, op2_max)) { tmp->overflow = 1; tmp->max = ZEND_LONG_MAX; + } else { + tmp->max = op1_max + op2_max; } return 1; } @@ -598,19 +600,21 @@ static int zend_inference_calc_binary_op_range( op2_min = OP2_MIN_RANGE(); op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - tmp->min = op1_min - op2_max; - tmp->max = op1_max - op2_min; if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_OVERFLOW() || - (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) { + zend_sub_will_overflow(op1_min, op2_max)) { tmp->underflow = 1; tmp->min = ZEND_LONG_MIN; + } else { + tmp->min = op1_min - op2_max; } if (OP1_RANGE_OVERFLOW() || OP2_RANGE_UNDERFLOW() || - (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) { + zend_sub_will_overflow(op1_max, op2_min)) { tmp->overflow = 1; tmp->max = ZEND_LONG_MAX; + } else { + tmp->max = op1_max - op2_min; } return 1; } diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h index de90850fc4..ec98fcbef9 100644 --- a/ext/opcache/Optimizer/zend_inference.h +++ b/ext/opcache/Optimizer/zend_inference.h @@ -238,6 +238,14 @@ DEFINE_SSA_OP_DEF_INFO(result) #define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1))) #define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline)) +static zend_always_inline zend_bool zend_add_will_overflow(zend_long a, zend_long b) { + return (b > 0 && a > ZEND_LONG_MAX - b) + || (b < 0 && a < ZEND_LONG_MIN - b); +} +static zend_always_inline zend_bool zend_sub_will_overflow(zend_long a, zend_long b) { + return (b > 0 && a < ZEND_LONG_MIN + b) + || (b < 0 && a > ZEND_LONG_MAX + b); +} BEGIN_EXTERN_C() diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index 1c70b596a7..dca4781382 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -221,15 +221,6 @@ static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_f } /* }}} */ -static inline zend_bool add_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a > ZEND_LONG_MAX - b) - || (b < 0 && a < ZEND_LONG_MIN - b); -} -static inline zend_bool sub_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a < ZEND_LONG_MIN + b) - || (b < 0 && a > ZEND_LONG_MAX + b); -} - /* e-SSA construction: Pi placement (Pi is actually a Phi with single * source and constraint). * Order of Phis is importent, Pis must be placed before Phis @@ -291,7 +282,7 @@ static void place_essa_pis( } if (var1 >= 0 && var2 >= 0) { - if (!sub_will_overflow(val1, val2) && !sub_will_overflow(val2, val1)) { + if (!zend_sub_will_overflow(val1, val2) && !zend_sub_will_overflow(val2, val1)) { zend_long tmp = val1; val1 -= val2; val2 -= tmp; @@ -316,7 +307,7 @@ static void place_essa_pis( } else { var1 = -1; } - if (!add_will_overflow(val2, add_val2)) { + if (!zend_add_will_overflow(val2, add_val2)) { val2 += add_val2; } else { var1 = -1; @@ -337,7 +328,7 @@ static void place_essa_pis( } else { var2 = -1; } - if (!add_will_overflow(val1, add_val1)) { + if (!zend_add_will_overflow(val1, add_val1)) { val1 += add_val1; } else { var2 = -1; diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 4b94f967bb..138168b16d 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -433,28 +433,26 @@ static int zend_may_overflow(const zend_op *opline, zend_op_array *op_array, zen return 1; } if (ssa->var_info[res].range.underflow) { - zend_long op1_min, op2_min, res_min; + zend_long op1_min, op2_min; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_min = OP1_MIN_RANGE(); op2_min = OP2_MIN_RANGE(); - res_min = op1_min + op2_min; - if (op1_min < 0 && op2_min < 0 && res_min >= 0) { + if (zend_add_will_overflow(op1_min, op2_min)) { return 1; } } if (ssa->var_info[res].range.overflow) { - zend_long op1_max, op2_max, res_max; + zend_long op1_max, op2_max; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - res_max = op1_max + op2_max; - if (op1_max > 0 && op2_max > 0 && res_max <= 0) { + if (zend_add_will_overflow(op1_max, op2_max)) { return 1; } } @@ -467,28 +465,26 @@ static int zend_may_overflow(const zend_op *opline, zend_op_array *op_array, zen return 1; } if (ssa->var_info[res].range.underflow) { - zend_long op1_min, op2_max, res_min; + zend_long op1_min, op2_max; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_min = OP1_MIN_RANGE(); op2_max = OP2_MAX_RANGE(); - res_min = op1_min - op2_max; - if (op1_min < 0 && op2_max > 0 && res_min >= 0) { + if (zend_sub_will_overflow(op1_min, op2_max)) { return 1; } } if (ssa->var_info[res].range.overflow) { - zend_long op1_max, op2_min, res_max; + zend_long op1_max, op2_min; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_max = OP1_MAX_RANGE(); op2_min = OP2_MIN_RANGE(); - res_max = op1_max - op2_min; - if (op1_max > 0 && op2_min < 0 && res_max <= 0) { + if (zend_sub_will_overflow(op1_max, op2_min)) { return 1; } } @@ -511,28 +507,26 @@ static int zend_may_overflow(const zend_op *opline, zend_op_array *op_array, zen return 1; } if (ssa->var_info[res].range.underflow) { - zend_long op1_min, op2_min, res_min; + zend_long op1_min, op2_min; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_min = OP1_MIN_RANGE(); op2_min = OP2_MIN_RANGE(); - res_min = op1_min + op2_min; - if (op1_min < 0 && op2_min < 0 && res_min >= 0) { + if (zend_add_will_overflow(op1_min, op2_min)) { return 1; } } if (ssa->var_info[res].range.overflow) { - zend_long op1_max, op2_max, res_max; + zend_long op1_max, op2_max; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - res_max = op1_max + op2_max; - if (op1_max > 0 && op2_max > 0 && res_max <= 0) { + if (zend_add_will_overflow(op1_max, op2_max)) { return 1; } } @@ -548,28 +542,26 @@ static int zend_may_overflow(const zend_op *opline, zend_op_array *op_array, zen return 1; } if (ssa->var_info[res].range.underflow) { - zend_long op1_min, op2_max, res_min; + zend_long op1_min, op2_max; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_min = OP1_MIN_RANGE(); op2_max = OP2_MAX_RANGE(); - res_min = op1_min - op2_max; - if (op1_min < 0 && op2_max > 0 && res_min >= 0) { + if (zend_sub_will_overflow(op1_min, op2_max)) { return 1; } } if (ssa->var_info[res].range.overflow) { - zend_long op1_max, op2_min, res_max; + zend_long op1_max, op2_min; if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { return 1; } op1_max = OP1_MAX_RANGE(); op2_min = OP2_MIN_RANGE(); - res_max = op1_max - op2_min; - if (op1_max > 0 && op2_min < 0 && res_max <= 0) { + if (zend_sub_will_overflow(op1_max, op2_min)) { return 1; } } -- 2.40.0