diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2018-02-09 15:10:23 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2018-02-09 15:10:23 +0100 |
commit | 7aac61ce762b2658263f605dd4d910f6914b7129 (patch) | |
tree | 1ae0b5d168c74db6c7f1f0eac195cd690598be63 | |
parent | f9a16d492e4ab5d9e98a68206b80a4753631d8c0 (diff) | |
download | php-git-7aac61ce762b2658263f605dd4d910f6914b7129.tar.gz |
Fixed bug #75938
New modulus range inference implementation has been verified using
https://gist.github.com/nikic/67947ff92cf0e1f7e931f2f0d4cf817f.
The computed bounds are not tight, but it seems to be very hard to
compute tight bounds on modulus operations.
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_inference.c | 53 | ||||
-rw-r--r-- | ext/opcache/tests/bug75938.phpt | 16 |
3 files changed, 62 insertions, 8 deletions
@@ -33,6 +33,7 @@ PHP NEWS . Fixed bug #75729 (opcache segfault when installing Bitrix). (Nikita) . Fixed bug #75893 (file_get_contents $http_response_header variable bugged with opcache). (Nikita) + . Fixed bug #75938 (Modulus value not stored in variable). (Nikita) - SPL: . Fixed bug #74519 (strange behavior of AppendIterator). (jhdxr) diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 2601b60f3c..9e58b9fd73 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -501,6 +501,28 @@ static void zend_ssa_range_and(zend_long a, zend_long b, zend_long c, zend_long } } +static inline zend_bool zend_abs_range( + zend_long min, zend_long max, zend_long *abs_min, zend_long *abs_max) { + if (min == ZEND_LONG_MIN) { + /* Cannot take absolute value of LONG_MIN */ + return 0; + } + + if (min >= 0) { + *abs_min = min; + *abs_max = max; + } else if (max <= 0) { + *abs_min = -max; + *abs_max = -min; + } else { + /* Range crossing zero */ + *abs_min = 0; + *abs_max = MAX(max, -min); + } + + return 1; +} + /* Get the normal op corresponding to a compound assignment op */ static inline zend_uchar get_compound_assign_op(zend_uchar opcode) { switch (opcode) { @@ -648,20 +670,35 @@ static int zend_inference_calc_binary_op_range( tmp->min = ZEND_LONG_MIN; tmp->max = ZEND_LONG_MAX; } else { + zend_long op2_abs_min, op2_abs_max; + op1_min = OP1_MIN_RANGE(); op2_min = OP2_MIN_RANGE(); op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - if (op2_min == 0 || op2_max == 0) { - /* avoid division by zero */ + if (!zend_abs_range(op2_min, op2_max, &op2_abs_min, &op2_abs_max)) { break; } - t1 = (op2_min == -1) ? 0 : (op1_min % op2_min); - t2 = (op2_max == -1) ? 0 : (op1_min % op2_max); - t3 = (op2_min == -1) ? 0 : (op1_max % op2_min); - t4 = (op2_max == -1) ? 0 : (op1_max % op2_max); - tmp->min = MIN(MIN(t1, t2), MIN(t3, t4)); - tmp->max = MAX(MAX(t1, t2), MAX(t3, t4)); + + if (op2_abs_max == 0) { + /* Always modulus by zero, nothing we can do */ + break; + } + if (op2_abs_min == 0) { + /* Ignore the modulus by zero case, which will throw */ + op2_abs_min++; + } + + if (op1_min >= 0) { + tmp->min = op1_max < op2_abs_min ? op1_min : 0; + tmp->max = MIN(op1_max, op2_abs_max - 1); + } else if (op1_max <= 0) { + tmp->min = MAX(op1_min, -op2_abs_max + 1); + tmp->max = op1_min > -op2_abs_min ? op1_max : 0; + } else { + tmp->min = MAX(op1_min, -op2_abs_max + 1); + tmp->max = MIN(op1_max, op2_abs_max - 1); + } } return 1; } diff --git a/ext/opcache/tests/bug75938.phpt b/ext/opcache/tests/bug75938.phpt new file mode 100644 index 0000000000..113745ff65 --- /dev/null +++ b/ext/opcache/tests/bug75938.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #75938: Modulus value not stored in variable +--FILE-- +<?php +function borken($columns) { + $columns = (int) $columns; + if ($columns < 1) return 0; + $count = count([1,2,3,4,5]); + var_dump($mod = ($count % $columns)); + var_dump($mod); +} +borken(2); +?> +--EXPECT-- +int(1) +int(1) |