From 1ed132e2e55112ab43125b45cbf3066072876c73 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 1 Apr 2020 13:02:58 +0200 Subject: [PATCH] Unify checks for binary operator errors for ct eval Move everything into one function and share it with opcache. This fixes some discrepancies. --- .../constant_expressions_exceptions_001.phpt | 6 +- .../runtime_compile_time_binary_operands.phpt | 7 +-- Zend/zend_compile.c | 56 ++++++++++--------- Zend/zend_compile.h | 2 +- ext/opcache/Optimizer/zend_optimizer.c | 39 +------------ 5 files changed, 36 insertions(+), 74 deletions(-) diff --git a/Zend/tests/constant_expressions_exceptions_001.phpt b/Zend/tests/constant_expressions_exceptions_001.phpt index 6a111d3aed..98e61765b3 100644 --- a/Zend/tests/constant_expressions_exceptions_001.phpt +++ b/Zend/tests/constant_expressions_exceptions_001.phpt @@ -3,8 +3,8 @@ Constant Expressions with unsupported operands 001 --FILE-- ---EXPECTF-- -Fatal error: Uncaught TypeError: Unsupported operand types: int + array in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Failed: 0 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ff466c5ef5..914f991bac 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7216,14 +7216,32 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_binary_op_produces_numeric_string_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */ +ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */ { + if ((opcode == ZEND_CONCAT || opcode == ZEND_FAST_CONCAT)) { + /* Array to string warning. */ + return Z_TYPE_P(op1) == IS_ARRAY || Z_TYPE_P(op2) == IS_ARRAY; + } + if (!(opcode == ZEND_ADD || opcode == ZEND_SUB || opcode == ZEND_MUL || opcode == ZEND_DIV - || opcode == ZEND_POW || opcode == ZEND_MOD || opcode == ZEND_SL || opcode == ZEND_SR - || opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR)) { + || opcode == ZEND_POW || opcode == ZEND_MOD || opcode == ZEND_SL || opcode == ZEND_SR + || opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR)) { + /* Only the numeric operations throw errors. */ return 0; } + if (Z_TYPE_P(op1) == IS_ARRAY || Z_TYPE_P(op2) == IS_ARRAY) { + if (opcode == ZEND_ADD && Z_TYPE_P(op1) == IS_ARRAY && Z_TYPE_P(op2) == IS_ARRAY) { + /* Adding two arrays is allowed. */ + return 0; + } + if (opcode == ZEND_ADD || opcode == ZEND_SUB || opcode == ZEND_MUL || opcode == ZEND_POW + || opcode == ZEND_DIV) { + /* These operators throw when one of the operands is an array. */ + return 1; + } + } + /* While basic arithmetic operators always produce numeric string errors, * bitwise operators don't produce errors if both operands are strings */ if ((opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR) @@ -7241,13 +7259,12 @@ ZEND_API zend_bool zend_binary_op_produces_numeric_string_error(uint32_t opcode, return 1; } - return 0; -} -/* }}} */ - -ZEND_API zend_bool zend_binary_op_produces_array_conversion_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */ -{ - if (opcode == ZEND_CONCAT && (Z_TYPE_P(op1) == IS_ARRAY || Z_TYPE_P(op2) == IS_ARRAY)) { + if ((opcode == ZEND_DIV || opcode == ZEND_MOD) && zval_get_long(op2) == 0) { + /* Division by zero throws an error. */ + return 1; + } + if ((opcode == ZEND_SL || opcode == ZEND_SR) && zval_get_long(op2) < 0) { + /* Shift by negative number throws an error. */ return 1; } @@ -7257,26 +7274,11 @@ ZEND_API zend_bool zend_binary_op_produces_array_conversion_error(uint32_t opcod static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */ { - binary_op_type fn = get_binary_op(opcode); - - /* don't evaluate division by zero at compile-time */ - if ((opcode == ZEND_DIV || opcode == ZEND_MOD) && - zval_get_long(op2) == 0) { - return 0; - } else if ((opcode == ZEND_SL || opcode == ZEND_SR) && - zval_get_long(op2) < 0) { - return 0; - } - - /* don't evaluate numeric string error-producing operations at compile-time */ - if (zend_binary_op_produces_numeric_string_error(opcode, op1, op2)) { - return 0; - } - /* don't evaluate array to string conversions at compile-time */ - if (zend_binary_op_produces_array_conversion_error(opcode, op1, op2)) { + if (zend_binary_op_produces_error(opcode, op1, op2)) { return 0; } + binary_op_type fn = get_binary_op(opcode); fn(result, op1, op2); return 1; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8a64ed86c3..d6ddb0760f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -1094,6 +1094,6 @@ END_EXTERN_C() /* The default value for CG(compiler_options) during eval() */ #define ZEND_COMPILE_DEFAULT_FOR_EVAL 0 -ZEND_API zend_bool zend_binary_op_produces_numeric_string_error(uint32_t opcode, zval *op1, zval *op2); +ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2); #endif /* ZEND_COMPILE_H */ diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 678d435a9a..facb238c21 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -54,47 +54,10 @@ int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zv binary_op_type binary_op = get_binary_op(opcode); int er, ret; - if (zend_binary_op_produces_numeric_string_error(opcode, op1, op2)) { - /* produces numeric string E_NOTICE/E_WARNING */ + if (zend_binary_op_produces_error(opcode, op1, op2)) { return FAILURE; } - switch (opcode) { - case ZEND_ADD: - if ((Z_TYPE_P(op1) == IS_ARRAY - || Z_TYPE_P(op2) == IS_ARRAY) - && Z_TYPE_P(op1) != Z_TYPE_P(op2)) { - /* produces "Unsupported operand types" exception */ - return FAILURE; - } - break; - case ZEND_DIV: - case ZEND_MOD: - if (zval_get_long(op2) == 0) { - /* division by 0 */ - return FAILURE; - } - /* break missing intentionally */ - case ZEND_SUB: - case ZEND_MUL: - case ZEND_POW: - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - if (Z_TYPE_P(op1) == IS_ARRAY - || Z_TYPE_P(op2) == IS_ARRAY) { - /* produces "Unsupported operand types" exception */ - return FAILURE; - } - break; - case ZEND_SL: - case ZEND_SR: - if (zval_get_long(op2) < 0) { - /* shift by negative number */ - return FAILURE; - } - break; - } - er = EG(error_reporting); EG(error_reporting) = 0; ret = binary_op(result, op1, op2); -- 2.50.1