]> granicus.if.org Git - php/commitdiff
Avoid UB in overflow checks
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 19 Jun 2019 09:58:42 +0000 (11:58 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 19 Jun 2019 10:02:42 +0000 (12:02 +0200)
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
ext/opcache/Optimizer/zend_inference.h
ext/opcache/Optimizer/zend_ssa.c
ext/opcache/jit/zend_jit.c

index 62a509dc6d5b65e7af2a91647c2d4a470448beff..a6475390a6a27d4dbc2116fd5ba79b2af9df3bfc 100644 (file)
@@ -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;
                        }
index de90850fc45e504531bee9c235c3dd71eeb34f0d..ec98fcbef9f8d9c2a4d405863b2c75bb9d9fc2a2 100644 (file)
@@ -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()
 
index 1c70b596a7b0a40fa6dff1be6b8b76007c22bff2..dca47813826f5ce78454eff0b58b5c6bc6088647 100644 (file)
@@ -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;
index 4b94f967bbd7283edbe264e6fdc076f679bacf0a..138168b16d9440029a6bb5eebef17faba95ed25e 100644 (file)
@@ -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;
                                }
                        }