From: Nikita Popov Date: Sun, 18 Feb 2018 16:20:32 +0000 (+0100) Subject: Fix ZEND_SL range inference X-Git-Tag: php-7.2.4RC1~62 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c0abab5fca9be3e978079955744a824911a613f0;p=php Fix ZEND_SL range inference This is a bit tricker than right shifts because shifting in the sign bit flips the sign. The computed bounds are not tight. --- diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 959b5148dd..3c154150d1 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -524,6 +524,15 @@ static inline zend_bool zend_abs_range( return 1; } +static inline zend_bool shift_left_overflows(zend_long n, zend_long s) { + /* This considers shifts that shift in the sign bit to be overflowing as well */ + if (n >= 0) { + return s >= SIZEOF_ZEND_LONG * 8 - 1 || (n << s) < n; + } else { + return s >= SIZEOF_ZEND_LONG * 8 - 1 || (n << s) > n; + } +} + /* Get the normal op corresponding to a compound assignment op */ static inline zend_uchar get_compound_assign_op(zend_uchar opcode) { switch (opcode) { @@ -717,12 +726,27 @@ static int zend_inference_calc_binary_op_range( op2_min = OP2_MIN_RANGE(); op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - t1 = op1_min << op2_min; - t2 = op1_min << op2_max; - t3 = op1_max << op2_min; - t4 = op1_max << op2_max; - tmp->min = MIN(MIN(t1, t2), MIN(t3, t4)); - tmp->max = MAX(MAX(t1, t2), MAX(t3, t4)); + + /* Shifts by negative numbers will throw, ignore them */ + if (op2_min < 0) { + op2_min = 0; + } + if (op2_max < 0) { + op2_max = 0; + } + + if (shift_left_overflows(op1_min, op2_max) + || shift_left_overflows(op1_max, op2_max)) { + tmp->min = ZEND_LONG_MIN; + tmp->max = ZEND_LONG_MAX; + } else { + t1 = op1_min << op2_min; + t2 = op1_min << op2_max; + t3 = op1_max << op2_min; + t4 = op1_max << op2_max; + tmp->min = MIN(MIN(t1, t2), MIN(t3, t4)); + tmp->max = MAX(MAX(t1, t2), MAX(t3, t4)); + } } return 1; }