mirror of
https://github.com/php/php-src.git
synced 2024-09-21 18:07:23 +00:00
Fix range inference hang
We shouldn't switch from range to no range for ZEND_DIV and instead explicitly return an overflowing range. Otherwise the range will not actually get updated during widening, and we'll perform essentially infinite narrowing. Fixes oss-fuzz #40566.
This commit is contained in:
parent
1a2fb90bf4
commit
f8ec5a1d4c
@ -656,35 +656,26 @@ static int zend_inference_calc_binary_op_range(
|
||||
op2_min = OP2_MIN_RANGE();
|
||||
op1_max = OP1_MAX_RANGE();
|
||||
op2_max = OP2_MAX_RANGE();
|
||||
if (op2_min <= 0 && op2_max >= 0) {
|
||||
/* If op2 crosses zero, then floating point values close to zero might be
|
||||
* possible, which will result in arbitrarily large results. As such, we can't
|
||||
* do anything useful in that case. */
|
||||
break;
|
||||
}
|
||||
if (op1_min == ZEND_LONG_MIN && op2_max == -1) {
|
||||
/* Avoid ill-defined division, which may trigger SIGFPE. */
|
||||
break;
|
||||
}
|
||||
|
||||
zend_long t1_, t2_, t3_, t4_;
|
||||
float_div(op1_min, op2_min, &t1, &t1_);
|
||||
float_div(op1_min, op2_max, &t2, &t2_);
|
||||
float_div(op1_max, op2_min, &t3, &t3_);
|
||||
float_div(op1_max, op2_max, &t4, &t4_);
|
||||
|
||||
/* The only case in which division can "overflow" either a division by an absolute
|
||||
* value smaller than one, or LONG_MIN / -1 in particular. Both cases have already
|
||||
* been excluded above. */
|
||||
if (OP1_RANGE_UNDERFLOW() ||
|
||||
OP2_RANGE_UNDERFLOW() ||
|
||||
OP1_RANGE_OVERFLOW() ||
|
||||
OP2_RANGE_OVERFLOW()) {
|
||||
/* If op2 crosses zero, then floating point values close to zero might be
|
||||
* possible, which will result in arbitrarily large results (overflow). Also
|
||||
* avoid dividing LONG_MIN by -1, which is UB. */
|
||||
if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_UNDERFLOW() ||
|
||||
OP1_RANGE_OVERFLOW() || OP2_RANGE_OVERFLOW() ||
|
||||
(op2_min <= 0 && op2_max >= 0) ||
|
||||
(op1_min == ZEND_LONG_MIN && op2_max == -1)
|
||||
) {
|
||||
tmp->underflow = 1;
|
||||
tmp->overflow = 1;
|
||||
tmp->min = ZEND_LONG_MIN;
|
||||
tmp->max = ZEND_LONG_MAX;
|
||||
} else {
|
||||
zend_long t1_, t2_, t3_, t4_;
|
||||
float_div(op1_min, op2_min, &t1, &t1_);
|
||||
float_div(op1_min, op2_max, &t2, &t2_);
|
||||
float_div(op1_max, op2_min, &t3, &t3_);
|
||||
float_div(op1_max, op2_max, &t4, &t4_);
|
||||
|
||||
tmp->min = MIN(MIN(MIN(t1, t2), MIN(t3, t4)), MIN(MIN(t1_, t2_), MIN(t3_, t4_)));
|
||||
tmp->max = MAX(MAX(MAX(t1, t2), MAX(t3, t4)), MAX(MAX(t1_, t2_), MAX(t3_, t4_)));
|
||||
}
|
||||
|
16
ext/opcache/tests/range_inference_hang.phpt
Normal file
16
ext/opcache/tests/range_inference_hang.phpt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
Range inference should not hang
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test() {
|
||||
$a = 0;
|
||||
while (true) {
|
||||
$a = $a+!$a=$a/!!~$a;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
===DONE===
|
||||
--EXPECT--
|
||||
===DONE===
|
Loading…
Reference in New Issue
Block a user