From 6079ba4b0ccad0ba4d781ff027fde096da0f8687 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 14 May 2016 13:54:41 +0200 Subject: [PATCH] Inference: Fix undef handling for binary ops We need to be careful about correctly handling that undef results in a null value. Otherwise, apart from simply generating incorrect results, we may also end up performing non-monotonic lattice transitions, thus causing an infinite type inference loop (see test). --- Zend/tests/inference_infinite_loop.phpt | 17 ++++++++++++++++ ext/opcache/Optimizer/zend_inference.c | 27 ++++++++++--------------- 2 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 Zend/tests/inference_infinite_loop.phpt diff --git a/Zend/tests/inference_infinite_loop.phpt b/Zend/tests/inference_infinite_loop.phpt new file mode 100644 index 0000000000..1e94ea8040 --- /dev/null +++ b/Zend/tests/inference_infinite_loop.phpt @@ -0,0 +1,17 @@ +--TEST-- +Type inference should not result in infinite loop +--FILE-- + +--EXPECTF-- +Notice: Undefined variable: a in %s on line %d diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index c247c29b3e..06f3cdc670 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -2236,12 +2236,12 @@ static uint32_t assign_dim_result_type( static uint32_t binary_op_result_type( zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, uint32_t result_var) { uint32_t tmp; + uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0); + uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0); switch (opcode) { case ZEND_ADD: tmp = MAY_BE_RC1; - if ((t1 & MAY_BE_ANY) == MAY_BE_LONG && - (t2 & MAY_BE_ANY) == MAY_BE_LONG) { - + if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) { if (!ssa->var_info[result_var].has_range || ssa->var_info[result_var].range.underflow || ssa->var_info[result_var].range.overflow) { @@ -2250,17 +2250,15 @@ static uint32_t binary_op_result_type( } else { tmp |= MAY_BE_LONG; } - } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE || - (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) { + } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) { tmp |= MAY_BE_DOUBLE; - } else if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY && - (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) { + } else if (t1_type == MAY_BE_ARRAY && t2_type == MAY_BE_ARRAY) { tmp |= MAY_BE_ARRAY; tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); } else { tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - if ((t1 & MAY_BE_ARRAY) && (t2 & MAY_BE_ARRAY)) { + if ((t1_type & MAY_BE_ARRAY) && (t2_type & MAY_BE_ARRAY)) { tmp |= MAY_BE_ARRAY; tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); @@ -2270,8 +2268,7 @@ static uint32_t binary_op_result_type( case ZEND_SUB: case ZEND_MUL: tmp = MAY_BE_RC1; - if ((t1 & MAY_BE_ANY) == MAY_BE_LONG && - (t2 & MAY_BE_ANY) == MAY_BE_LONG) { + if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) { if (!ssa->var_info[result_var].has_range || ssa->var_info[result_var].range.underflow || ssa->var_info[result_var].range.overflow) { @@ -2280,8 +2277,7 @@ static uint32_t binary_op_result_type( } else { tmp |= MAY_BE_LONG; } - } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE || - (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) { + } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) { tmp |= MAY_BE_DOUBLE; } else { tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; @@ -2290,8 +2286,7 @@ static uint32_t binary_op_result_type( case ZEND_DIV: case ZEND_POW: tmp = MAY_BE_RC1; - if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE || - (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) { + if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) { tmp |= MAY_BE_DOUBLE; } else { tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; @@ -2307,10 +2302,10 @@ static uint32_t binary_op_result_type( case ZEND_BW_AND: case ZEND_BW_XOR: tmp = MAY_BE_RC1; - if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) { + if ((t1_type & MAY_BE_STRING) && (t2_type & MAY_BE_STRING)) { tmp |= MAY_BE_STRING; } - if ((t1 & (MAY_BE_ANY-MAY_BE_STRING)) || (t2 & (MAY_BE_ANY-MAY_BE_STRING))) { + if ((t1_type & ~MAY_BE_STRING) || (t2_type & ~MAY_BE_STRING)) { tmp |= MAY_BE_LONG; } break; -- 2.50.1