diff --git a/NEWS b/NEWS index f677f0a0d37..dfbc707d7ab 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,11 @@ PHP NEWS . Added support for negative string offsets in string offset syntax and various string functions. (Francois) . Added a form of the list() construct where keys can be specified. (Andrea) + . Number operators taking numeric strings now emit E_NOTICEs or E_WARNINGs + when given malformed numeric strings. (Andrea) + . (int), intval() where $base is 10 or unspecified, settype(), decbin(), + decoct(), dechex(), integer operators and other conversions now always + respect scientific notation in numeric strings. (Andrea) - FTP: . Implemented FR #55651 (Option to ignore the returned FTP PASV address). diff --git a/UPGRADING b/UPGRADING index a938978d7bf..9c94fd09a92 100644 --- a/UPGRADING +++ b/UPGRADING @@ -21,6 +21,10 @@ PHP 7.1 UPGRADE NOTES - Core: . 'void' can no longer be used as the name of a class, interface, or trait. This applies to declarations, class_alias() and use statements. + . (int), intval() where $base is 10 or unspecified, settype(), decbin(), + decoct(), dechex(), integer operators and other conversions now always + respect scientific notation in numeric strings. + (RFC: https://wiki.php.net/rfc/invalid_strings_in_arithmetic) - JSON: . When calling json_encode with JSON_UNESCAPED_UNICODE option, U+2028 and @@ -37,6 +41,17 @@ PHP 7.1 UPGRADE NOTES (RFC: https://wiki.php.net/rfc/negative-string-offsets) . Added a form of the list() construct where keys can be specified. (RFC: https://wiki.php.net/rfc/list_keys) + . Number operators taking numeric strings now emit "A non well formed numeric + value encountered" E_NOTICEs for leading-numeric strings, and "A + non-numeric value encountered" E_WARNINGs for non-numeric strings. + This always applies to the +, -, *, /, **, %, << and >> operators, and + their assignment counterparts +=, -=, *=, /=, **=, %=, <<= and >>=. + For the bitwise operators |, & and ^, and their assignment counterparts + |=, &= and ^=, this only applies where only one operand is a string. + Note that this never applies to the bitwise NOT operator, ~, which does not + handle numeric strings, nor to the increment and decrement operators + ++ and --, which have a unique approach to handling numeric strings. + (RFC: https://wiki.php.net/rfc/invalid_strings_in_arithmetic) ======================================== 3. Changes in SAPI modules diff --git a/Zend/tests/add_006.phpt b/Zend/tests/add_006.phpt index d56df2f329c..fe1c0830e25 100644 --- a/Zend/tests/add_006.phpt +++ b/Zend/tests/add_006.phpt @@ -38,11 +38,19 @@ var_dump($c); echo "Done\n"; ?> --EXPECTF-- + +Warning: A non-numeric value encountered in %s on line %d int(75636) + +Notice: A non well formed numeric value encountered in %s on line %d int(951858) int(48550510) float(75661.68) + +Warning: A non-numeric value encountered in %s on line %d int(75636) + +Notice: A non well formed numeric value encountered in %s on line %d int(951858) int(48550510) float(75661.68) diff --git a/Zend/tests/add_007.phpt b/Zend/tests/add_007.phpt index 66f54057060..089b24ae0b5 100644 --- a/Zend/tests/add_007.phpt +++ b/Zend/tests/add_007.phpt @@ -19,8 +19,13 @@ var_dump($c); echo "Done\n"; ?> --EXPECTF-- + +Warning: A non-numeric value encountered in %s on line %d + Exception: Unsupported operand types +Warning: A non-numeric value encountered in %s on line %d + Fatal error: Uncaught Error: Unsupported operand types in %s:%d Stack trace: #0 {main} diff --git a/Zend/tests/closure_invoke_case_insensitive.phpt b/Zend/tests/closure_invoke_case_insensitive.phpt new file mode 100644 index 00000000000..d41d58a747c --- /dev/null +++ b/Zend/tests/closure_invoke_case_insensitive.phpt @@ -0,0 +1,16 @@ +--TEST-- +Closure::__invoke() is case insensitive +--FILE-- +__INVOKE($n); +var_dump($n); + +?> +--EXPECT-- +int(2) diff --git a/Zend/tests/constant_expressions_dynamic.phpt b/Zend/tests/constant_expressions_dynamic.phpt index d4e06ee258b..b0ba3a5b194 100644 --- a/Zend/tests/constant_expressions_dynamic.phpt +++ b/Zend/tests/constant_expressions_dynamic.phpt @@ -42,7 +42,9 @@ var_dump( ); ?> ---EXPECT-- +--EXPECTF-- + +Warning: A non-numeric value encountered in %s on line %d int(3) string(4) "1foo" bool(false) diff --git a/Zend/tests/int_conversion_exponents.phpt b/Zend/tests/int_conversion_exponents.phpt new file mode 100644 index 00000000000..d924cb7b81e --- /dev/null +++ b/Zend/tests/int_conversion_exponents.phpt @@ -0,0 +1,52 @@ +--TEST-- +Integer conversion from scientific notation +--FILE-- + +--EXPECTF-- +int(1234500000) +int(-1234500000) +int(1234500000) +int(-1234500000) +int(1234500000) +int(-1234500000) +int(1234500000) +int(-1234500000) + +int(1234500000) +int(-1234500000) +int(1234500000) +int(-1234500000) + +Notice: A non well formed numeric value encountered in %s on line %d +int(1234500000) + +Notice: A non well formed numeric value encountered in %s on line %d +int(-1234500000) + +Notice: A non well formed numeric value encountered in %s on line %d +int(1234500000) + +Notice: A non well formed numeric value encountered in %s on line %d +int(-1234500000) diff --git a/Zend/tests/numeric_string_errors.phpt b/Zend/tests/numeric_string_errors.phpt new file mode 100644 index 00000000000..e98c58dda72 --- /dev/null +++ b/Zend/tests/numeric_string_errors.phpt @@ -0,0 +1,195 @@ +--TEST-- +Invalid numeric string E_WARNINGs and E_NOTICEs +--FILE-- +> "53 ullamco"); +var_dump("laboris" >> "nisi"); +echo "---", PHP_EOL; +var_dump("59 ut" | 61); +var_dump(67 | "71 aliquip"); +var_dump("ex" | 73); +var_dump(79 | "ea"); +echo "---", PHP_EOL; +var_dump("83 commodo" & 89); +var_dump(97 & "101 consequat."); +var_dump("Duis" & 103); +var_dump(107 & "aute"); +echo "---", PHP_EOL; +var_dump("109 irure" ^ 113); +var_dump(127 ^ "131 dolor"); +var_dump("in" ^ 137); +var_dump(139 ^ "reprehenderit"); +echo "---", PHP_EOL; +var_dump(+"149 in"); +var_dump(+"voluptate"); +echo "---", PHP_EOL; +var_dump(-"151 velit"); +var_dump(-"esse"); +?> +--EXPECTF-- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(5) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(-2) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(143) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +float(0.89473684210526) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d + +Warning: Division by zero in %s on line %d +float(NAN) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +float(3.0910586430935E+39) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(1) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(31) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(%d) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(0) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(63) + +Notice: A non well formed numeric value encountered in %s on line %d +int(71) + +Warning: A non-numeric value encountered in %s on line %d +int(73) + +Warning: A non-numeric value encountered in %s on line %d +int(79) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(81) + +Notice: A non well formed numeric value encountered in %s on line %d +int(97) + +Warning: A non-numeric value encountered in %s on line %d +int(0) + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(28) + +Notice: A non well formed numeric value encountered in %s on line %d +int(252) + +Warning: A non-numeric value encountered in %s on line %d +int(137) + +Warning: A non-numeric value encountered in %s on line %d +int(139) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(149) + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(-151) + +Warning: A non-numeric value encountered in %s on line %d +int(0) diff --git a/Zend/tests/numeric_string_errors_assign.phpt b/Zend/tests/numeric_string_errors_assign.phpt new file mode 100644 index 00000000000..8d882aadccd --- /dev/null +++ b/Zend/tests/numeric_string_errors_assign.phpt @@ -0,0 +1,236 @@ +--TEST-- +Invalid numeric string E_WARNINGs and E_NOTICEs, combined assignment operations +--FILE-- +>= "53 ullamco"; +var_dump($a); +$a = foxcache("laboris"); +$a >>= "nisi"; +var_dump($a); +echo "---", PHP_EOL; +$a = foxcache("59 ut"); +$a |= 61; +var_dump($a); +$a = foxcache(67); +$a |= "71 aliquip"; +var_dump($a); +$a = foxcache("ex"); +$a |= 73; +var_dump($a); +$a = foxcache(79); +$a |= "ea"; +var_dump($a); +echo "---", PHP_EOL; +$a = foxcache("83 commodo"); +$a &= 89; +var_dump($a); +$a = foxcache(97); +$a &= "101 consequat."; +var_dump($a); +$a = foxcache("Duis"); +$a &= 103; +var_dump($a); +$a = foxcache(107); +$a &= "aute"; +var_dump($a); +echo "---", PHP_EOL; +$a = foxcache("109 irure"); +$a ^= 113; +var_dump($a); +$a = foxcache(127); +$a ^= "131 dolor"; +var_dump($a); +$a = foxcache("in"); +$a ^= 137; +var_dump($a); +$a = foxcache(139); +$a ^= "reprehenderit"; +var_dump($a); +?> +--EXPECTF-- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(5) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(-2) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(143) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +float(0.89473684210526) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d + +Warning: Division by zero in %s on line %d +float(NAN) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +float(3.0910586430935E+39) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(1) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(31) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(%d) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d +int(0) + +Warning: A non-numeric value encountered in %s on line %d + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(63) + +Notice: A non well formed numeric value encountered in %s on line %d +int(71) + +Warning: A non-numeric value encountered in %s on line %d +int(73) + +Warning: A non-numeric value encountered in %s on line %d +int(79) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(81) + +Notice: A non well formed numeric value encountered in %s on line %d +int(97) + +Warning: A non-numeric value encountered in %s on line %d +int(0) + +Warning: A non-numeric value encountered in %s on line %d +int(0) +--- + +Notice: A non well formed numeric value encountered in %s on line %d +int(28) + +Notice: A non well formed numeric value encountered in %s on line %d +int(252) + +Warning: A non-numeric value encountered in %s on line %d +int(137) + +Warning: A non-numeric value encountered in %s on line %d +int(139) diff --git a/Zend/tests/self_and.phpt b/Zend/tests/self_and.phpt index cdcde77992b..44db877e929 100644 --- a/Zend/tests/self_and.phpt +++ b/Zend/tests/self_and.phpt @@ -20,6 +20,10 @@ echo "Done\n"; ?> --EXPECTF-- int(18) + +Warning: A non-numeric value encountered in %s on line %d int(0) + +Notice: A non well formed numeric value encountered in %s on line %d int(33) Done diff --git a/Zend/tests/self_mod.phpt b/Zend/tests/self_mod.phpt index 19e45d88fc4..0b10987aeb8 100644 --- a/Zend/tests/self_mod.phpt +++ b/Zend/tests/self_mod.phpt @@ -20,6 +20,10 @@ echo "Done\n"; ?> --EXPECTF-- int(13) + +Warning: A non-numeric value encountered in %s on line %d int(0) + +Notice: A non well formed numeric value encountered in %s on line %d int(3) Done diff --git a/Zend/tests/self_or.phpt b/Zend/tests/self_or.phpt index ae667bff16a..8ace518bde5 100644 --- a/Zend/tests/self_or.phpt +++ b/Zend/tests/self_or.phpt @@ -20,6 +20,10 @@ echo "Done\n"; ?> --EXPECTF-- int(127) + +Warning: A non-numeric value encountered in %s on line %d int(11) + +Notice: A non well formed numeric value encountered in %s on line %d int(45345) Done diff --git a/Zend/tests/self_xor.phpt b/Zend/tests/self_xor.phpt index a7e43f539d6..c097930d6dd 100644 --- a/Zend/tests/self_xor.phpt +++ b/Zend/tests/self_xor.phpt @@ -20,6 +20,10 @@ echo "Done\n"; ?> --EXPECTF-- int(109) + +Warning: A non-numeric value encountered in %s on line %d int(11) + +Notice: A non well formed numeric value encountered in %s on line %d int(45312) Done diff --git a/Zend/tests/shift_001.phpt b/Zend/tests/shift_001.phpt index aeb399452d5..7546f1a6d8f 100644 --- a/Zend/tests/shift_001.phpt +++ b/Zend/tests/shift_001.phpt @@ -20,6 +20,10 @@ echo "Done\n"; ?> --EXPECTF-- int(492) + +Warning: A non-numeric value encountered in %s on line %d int(0) + +Notice: A non well formed numeric value encountered in %s on line %d int(362760) Done diff --git a/Zend/tests/shift_002.phpt b/Zend/tests/shift_002.phpt index 4d8421a5666..6288152585f 100644 --- a/Zend/tests/shift_002.phpt +++ b/Zend/tests/shift_002.phpt @@ -20,6 +20,10 @@ echo "Done\n"; ?> --EXPECTF-- int(30) + +Warning: A non-numeric value encountered in %s on line %d int(0) + +Notice: A non well formed numeric value encountered in %s on line %d int(5668) Done diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5ebfe2cb178..18e6e0f4f4a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -370,11 +370,7 @@ ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_weak(zval *arg, zend_long *de if (UNEXPECTED(zend_isnan(Z_DVAL_P(arg)))) { return 0; } - if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(Z_DVAL_P(arg)))) { - *dest = (Z_DVAL_P(arg) > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN; - } else { - *dest = zend_dval_to_lval(Z_DVAL_P(arg)); - } + *dest = zend_dval_to_lval_cap(Z_DVAL_P(arg)); } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { double d; int type; @@ -384,11 +380,7 @@ ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_weak(zval *arg, zend_long *de if (UNEXPECTED(zend_isnan(d))) { return 0; } - if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(d))) { - *dest = (d > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN; - } else { - *dest = zend_dval_to_lval(d); - } + *dest = zend_dval_to_lval_cap(d); } else { return 0; } diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 55bea0b9a8c..d52d0b5c257 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -297,14 +297,10 @@ ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */ static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */ { - zend_string *lc_name; - - lc_name = zend_string_tolower(method); - if (zend_string_equals_literal(method, ZEND_INVOKE_FUNC_NAME)) { - zend_string_release(lc_name); + if (zend_string_equals_literal_ci(method, ZEND_INVOKE_FUNC_NAME)) { return zend_get_closure_invoke_method(*object); } - zend_string_release(lc_name); + return std_object_handlers.get_method(object, method, key); } /* }}} */ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2e1ce9ba448..1401a6e051a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6237,6 +6237,35 @@ 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) /* {{{ */ +{ + 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)) { + return 0; + } + + /* 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) + && Z_TYPE_P(op1) == IS_STRING && Z_TYPE_P(op2) == IS_STRING) { + return 0; + } + + if (Z_TYPE_P(op1) == IS_STRING + && !is_numeric_string(Z_STRVAL_P(op1), Z_STRLEN_P(op1), NULL, NULL, 0)) { + return 1; + } + + if (Z_TYPE_P(op2) == IS_STRING + && !is_numeric_string(Z_STRVAL_P(op2), Z_STRLEN_P(op2), NULL, NULL, 0)) { + return 1; + } + + return 0; +} +/* }}} */ + 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); @@ -6250,6 +6279,11 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode 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; + } + fn(result, op1, op2); return 1; } @@ -6262,11 +6296,11 @@ static inline void zend_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op } /* }}} */ -static inline void zend_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */ +static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */ { zval left; ZVAL_LONG(&left, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1); - mul_function(result, &left, op); + return zend_try_ct_eval_binary_op(result, ZEND_MUL, &left, op); } /* }}} */ @@ -6464,10 +6498,11 @@ void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); if (expr_node.op_type == IS_CONST) { - result->op_type = IS_CONST; - zend_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant); - zval_ptr_dtor(&expr_node.u.constant); - return; + if (zend_try_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant)) { + result->op_type = IS_CONST; + zval_ptr_dtor(&expr_node.u.constant); + return; + } } lefthand_node.op_type = IS_CONST; @@ -7802,7 +7837,9 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ return; } - zend_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0])); + if (!zend_try_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0]))) { + return; + } break; case ZEND_AST_CONDITIONAL: { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index f3dc8081b8a..b3ba352df88 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -1023,6 +1023,8 @@ 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); + #endif /* ZEND_COMPILE_H */ /* diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 50557e56d80..1710ba44dd1 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -147,7 +147,7 @@ static zend_always_inline void zend_unwrap_reference(zval *op) /* {{{ */ } /* }}} */ -ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op) /* {{{ */ +void ZEND_FASTCALL _convert_scalar_to_number(zval *op, zend_bool silent) /* {{{ */ { try_again: switch (Z_TYPE_P(op)) { @@ -159,8 +159,11 @@ try_again: zend_string *str; str = Z_STR_P(op); - if ((Z_TYPE_INFO_P(op)=is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &Z_LVAL_P(op), &Z_DVAL_P(op), 1)) == 0) { + if ((Z_TYPE_INFO_P(op)=is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &Z_LVAL_P(op), &Z_DVAL_P(op), silent ? 1 : -1)) == 0) { ZVAL_LONG(op, 0); + if (!silent) { + zend_error(E_WARNING, "A non-numeric value encountered"); + } } zend_string_release(str); break; @@ -186,18 +189,27 @@ try_again: } /* }}} */ +ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op) /* {{{ */ +{ + _convert_scalar_to_number(op, 1); +} +/* }}} */ + /* {{{ zendi_convert_scalar_to_number */ -#define zendi_convert_scalar_to_number(op, holder, result) \ +#define zendi_convert_scalar_to_number(op, holder, result, silent) \ if (op==result) { \ if (Z_TYPE_P(op) != IS_LONG) { \ - convert_scalar_to_number(op); \ + _convert_scalar_to_number(op, silent); \ } \ } else { \ switch (Z_TYPE_P(op)) { \ case IS_STRING: \ { \ - if ((Z_TYPE_INFO(holder)=is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), 1)) == 0) { \ + if ((Z_TYPE_INFO(holder)=is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), silent ? 1 : -1)) == 0) { \ ZVAL_LONG(&(holder), 0); \ + if (!silent) { \ + zend_error(E_WARNING, "A non-numeric value encountered"); \ + } \ } \ (op) = &(holder); \ break; \ @@ -258,7 +270,7 @@ try_again: } \ } \ ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(op, op_func); \ - op1_lval = _zval_get_long_func(op1); \ + op1_lval = _zval_get_long_func_noisy(op1); \ } else { \ op1_lval = Z_LVAL_P(op1); \ } \ @@ -273,7 +285,7 @@ try_again: } \ } \ ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(op); \ - op2_lval = _zval_get_long_func(op2); \ + op2_lval = _zval_get_long_func_noisy(op2); \ } else { \ op2_lval = Z_LVAL_P(op2); \ } \ @@ -313,8 +325,11 @@ try_again: case IS_STRING: { zend_string *str = Z_STR_P(op); - - ZVAL_LONG(op, ZEND_STRTOL(ZSTR_VAL(str), NULL, base)); + if (base == 10) { + ZVAL_LONG(op, zval_get_long(op)); + } else { + ZVAL_LONG(op, ZEND_STRTOL(ZSTR_VAL(str), NULL, base)); + } zend_string_release(str); } break; @@ -728,7 +743,7 @@ ZEND_API void multi_convert_to_string_ex(int argc, ...) /* {{{ */ } /* }}} */ -ZEND_API zend_long ZEND_FASTCALL _zval_get_long_func(zval *op) /* {{{ */ +static zend_always_inline zend_long ZEND_FASTCALL _zval_get_long_func_ex(zval *op, zend_bool silent) /* {{{ */ { try_again: switch (Z_TYPE_P(op)) { @@ -744,7 +759,26 @@ try_again: case IS_DOUBLE: return zend_dval_to_lval(Z_DVAL_P(op)); case IS_STRING: - return ZEND_STRTOL(Z_STRVAL_P(op), NULL, 10); + { + zend_uchar type; + zend_long lval; + double dval; + if (0 == (type = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, silent ? 1 : -1))) { + if (!silent) { + zend_error(E_WARNING, "A non-numeric value encountered"); + } + return 0; + } else if (EXPECTED(type == IS_LONG)) { + return lval; + } else { + /* Previously we used strtol here, not is_numeric_string, + * and strtol gives you LONG_MAX/_MIN on overflow. + * We use use saturating conversion to emulate strtol()'s + * behaviour. + */ + return zend_dval_to_lval_cap(dval); + } + } case IS_ARRAY: return zend_hash_num_elements(Z_ARRVAL_P(op)) ? 1 : 0; case IS_OBJECT: @@ -766,6 +800,18 @@ try_again: } /* }}} */ +ZEND_API zend_long ZEND_FASTCALL _zval_get_long_func(zval *op) /* {{{ */ +{ + return _zval_get_long_func_ex(op, 1); +} +/* }}} */ + +static zend_long ZEND_FASTCALL _zval_get_long_func_noisy(zval *op) /* {{{ */ +{ + return _zval_get_long_func_ex(op, 0); +} +/* }}} */ + ZEND_API double ZEND_FASTCALL _zval_get_double_func(zval *op) /* {{{ */ { try_again: @@ -916,8 +962,8 @@ ZEND_API int ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2) /* { } else if (!converted) { ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_ADD, add_function); - zendi_convert_scalar_to_number(op1, op1_copy, result); - zendi_convert_scalar_to_number(op2, op2_copy, result); + zendi_convert_scalar_to_number(op1, op1_copy, result, 0); + zendi_convert_scalar_to_number(op2, op2_copy, result, 0); converted = 1; } else { zend_throw_error(NULL, "Unsupported operand types"); @@ -969,8 +1015,8 @@ ZEND_API int ZEND_FASTCALL sub_function(zval *result, zval *op1, zval *op2) /* { } else if (!converted) { ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_SUB, sub_function); - zendi_convert_scalar_to_number(op1, op1_copy, result); - zendi_convert_scalar_to_number(op2, op2_copy, result); + zendi_convert_scalar_to_number(op1, op1_copy, result, 0); + zendi_convert_scalar_to_number(op2, op2_copy, result, 0); converted = 1; } else { zend_throw_error(NULL, "Unsupported operand types"); @@ -1016,8 +1062,8 @@ ZEND_API int ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *op2) /* { } else if (!converted) { ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_MUL, mul_function); - zendi_convert_scalar_to_number(op1, op1_copy, result); - zendi_convert_scalar_to_number(op2, op2_copy, result); + zendi_convert_scalar_to_number(op1, op1_copy, result, 0); + zendi_convert_scalar_to_number(op2, op2_copy, result, 0); converted = 1; } else { zend_throw_error(NULL, "Unsupported operand types"); @@ -1098,13 +1144,13 @@ ZEND_API int ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *op2) /* { ZVAL_LONG(result, 0); return SUCCESS; } else { - zendi_convert_scalar_to_number(op1, op1_copy, result); + zendi_convert_scalar_to_number(op1, op1_copy, result, 0); } if (Z_TYPE_P(op2) == IS_ARRAY) { ZVAL_LONG(result, 1L); return SUCCESS; } else { - zendi_convert_scalar_to_number(op2, op2_copy, result); + zendi_convert_scalar_to_number(op2, op2_copy, result, 0); } converted = 1; } else { @@ -1169,8 +1215,8 @@ ZEND_API int ZEND_FASTCALL div_function(zval *result, zval *op1, zval *op2) /* { } else if (!converted) { ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_DIV, div_function); - zendi_convert_scalar_to_number(op1, op1_copy, result); - zendi_convert_scalar_to_number(op2, op2_copy, result); + zendi_convert_scalar_to_number(op1, op1_copy, result, 0); + zendi_convert_scalar_to_number(op2, op2_copy, result, 0); converted = 1; } else { zend_throw_error(NULL, "Unsupported operand types"); @@ -1377,13 +1423,13 @@ ZEND_API int ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, zval *op if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_OR, bitwise_or_function); - op1_lval = _zval_get_long_func(op1); + op1_lval = _zval_get_long_func_noisy(op1); } else { op1_lval = Z_LVAL_P(op1); } if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_OR); - op2_lval = _zval_get_long_func(op2); + op2_lval = _zval_get_long_func_noisy(op2); } else { op2_lval = Z_LVAL_P(op2); } @@ -1444,13 +1490,13 @@ ZEND_API int ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, zval *o if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_AND, bitwise_and_function); - op1_lval = _zval_get_long_func(op1); + op1_lval = _zval_get_long_func_noisy(op1); } else { op1_lval = Z_LVAL_P(op1); } if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_AND); - op2_lval = _zval_get_long_func(op2); + op2_lval = _zval_get_long_func_noisy(op2); } else { op2_lval = Z_LVAL_P(op2); } @@ -1511,13 +1557,13 @@ ZEND_API int ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *o if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_XOR, bitwise_xor_function); - op1_lval = _zval_get_long_func(op1); + op1_lval = _zval_get_long_func_noisy(op1); } else { op1_lval = Z_LVAL_P(op1); } if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_XOR); - op2_lval = _zval_get_long_func(op2); + op2_lval = _zval_get_long_func_noisy(op2); } else { op2_lval = Z_LVAL_P(op2); } @@ -1944,8 +1990,8 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) ZVAL_LONG(result, zval_is_true(op1) ? 0 : -1); return SUCCESS; } else { - zendi_convert_scalar_to_number(op1, op1_copy, result); - zendi_convert_scalar_to_number(op2, op2_copy, result); + zendi_convert_scalar_to_number(op1, op1_copy, result, 1); + zendi_convert_scalar_to_number(op2, op2_copy, result, 1); converted = 1; } } else if (Z_TYPE_P(op1)==IS_ARRAY) { diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index b0b167bd747..db6162a4e52 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -122,6 +122,16 @@ static zend_always_inline zend_long zend_dval_to_lval(double d) return (zend_long)d; } #endif + +static zend_always_inline zend_long zend_dval_to_lval_cap(double d) +{ + if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) { + return 0; + } else if (!ZEND_DOUBLE_FITS_LONG(d)) { + return (d > 0 ? ZEND_LONG_MAX : ZEND_LONG_MIN); + } + return (zend_long)d; +} /* }}} */ #define ZEND_IS_DIGIT(c) ((c) >= '0' && (c) <= '9') diff --git a/ext/fileinfo/libmagic/funcs.c b/ext/fileinfo/libmagic/funcs.c index 6ade713efaf..9ea5386ebf9 100644 --- a/ext/fileinfo/libmagic/funcs.c +++ b/ext/fileinfo/libmagic/funcs.c @@ -403,7 +403,7 @@ file_check_mem(struct magic_set *ms, unsigned int level) size_t len; if (level >= ms->c.len) { - len = (ms->c.len += 20 + level) * sizeof(*ms->c.li); + len = (ms->c.len = 20 + level) * sizeof(*ms->c.li); ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ? emalloc(len) : erealloc(ms->c.li, len)); diff --git a/ext/fileinfo/tests/bug68996.phpt b/ext/fileinfo/tests/bug68996.phpt index da208d35bcc..214e52fa6f7 100644 --- a/ext/fileinfo/tests/bug68996.phpt +++ b/ext/fileinfo/tests/bug68996.phpt @@ -1,11 +1,14 @@ --TEST-- Bug #68996 (Invalid free of CG(interned_empty_string)) --SKIPIF-- + --INI-- html_errors=1 ---ENV-- -USE_ZEND_ALLOC=0 --FILE-- opcode == ZEND_SL || opline->opcode == ZEND_SR) && zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) { SET_VAR_SOURCE(opline); + opline++; + continue; + } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) { + SET_VAR_SOURCE(opline); opline++; continue; } + printf("%d\n", opline->opcode); er = EG(error_reporting); EG(error_reporting) = 0; if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) { diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index ae31504f4c0..71f879e958a 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -84,6 +84,9 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) { /* shift by negative number */ break; + } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) { + /* produces numeric string E_NOTICE/E_WARNING */ + break; } er = EG(error_reporting); EG(error_reporting) = 0; diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c index d2a2064a787..2be97ed9382 100644 --- a/ext/opcache/Optimizer/pass2.c +++ b/ext/opcache/Optimizer/pass2.c @@ -48,7 +48,10 @@ void zend_optimizer_pass2(zend_op_array *op_array) case ZEND_POW: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { - convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); + /* don't optimise if it should produce a runtime numeric string error */ + if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) { + convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); + } } } /* break missing *intentionally* - the assign_op's may only optimize op2 */ @@ -63,7 +66,10 @@ void zend_optimizer_pass2(zend_op_array *op_array) } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); + /* don't optimise if it should produce a runtime numeric string error */ + if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { + convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); + } } } break; @@ -73,7 +79,11 @@ void zend_optimizer_pass2(zend_op_array *op_array) case ZEND_SR: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) { - convert_to_long(&ZEND_OP1_LITERAL(opline)); + /* don't optimise if it should produce a runtime numeric string error */ + if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING + && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) { + convert_to_long(&ZEND_OP1_LITERAL(opline)); + } } } /* break missing *intentionally - the assign_op's may only optimize op2 */ @@ -86,7 +96,11 @@ void zend_optimizer_pass2(zend_op_array *op_array) } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { - convert_to_long(&ZEND_OP2_LITERAL(opline)); + /* don't optimise if it should produce a runtime numeric string error */ + if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING + && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) { + convert_to_long(&ZEND_OP2_LITERAL(opline)); + } } } break; diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c index bbc6fb54f93..ef586e12a73 100644 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ b/ext/opcache/Optimizer/zend_call_graph.c @@ -260,6 +260,8 @@ static void zend_analyze_recursion(zend_call_graph *call_graph) call_info = call_info->next_caller; } } + + free_alloca(visited, use_heap); } static void zend_sort_op_arrays(zend_call_graph *call_graph) diff --git a/ext/shmop/shmop.c b/ext/shmop/shmop.c index 4fe65ae37d0..d647790aa06 100644 --- a/ext/shmop/shmop.c +++ b/ext/shmop/shmop.c @@ -203,6 +203,7 @@ PHP_FUNCTION(shmop_open) } if (shmctl(shmop->shmid, IPC_STAT, &shm)) { + /* please do not add coverage here: the segment would be leaked and impossible to delete via php */ php_error_docref(NULL, E_WARNING, "unable to get shared memory segment information '%s'", strerror(errno)); goto err; } diff --git a/ext/shmop/tests/003.phpt b/ext/shmop/tests/003.phpt deleted file mode 100644 index 1c154f08ca0..00000000000 --- a/ext/shmop/tests/003.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -shmop extension error messages (non-root) ---SKIPIF-- - ---FILE-- - ---EXPECTF-- -## shmop_open function tests ## - -Warning: shmop_open(): unable to get shared memory segment information 'Permission denied' in %s on line %d -bool(false) diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index d5cb32606ea..60cbac57269 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -82,18 +82,18 @@ static inline spl_array_object *spl_array_from_obj(zend_object *obj) /* {{{ */ { #define Z_SPLARRAY_P(zv) spl_array_from_obj(Z_OBJ_P((zv))) -static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /* {{{ */ +static inline HashTable **spl_array_get_hash_table_ptr(spl_array_object* intern) { /* {{{ */ //??? TODO: Delay duplication for arrays; only duplicate for write operations if (intern->ar_flags & SPL_ARRAY_IS_SELF) { if (!intern->std.properties) { rebuild_object_properties(&intern->std); } - return intern->std.properties; + return &intern->std.properties; } else if (intern->ar_flags & SPL_ARRAY_USE_OTHER) { spl_array_object *other = Z_SPLARRAY_P(&intern->array); - return spl_array_get_hash_table(other); + return spl_array_get_hash_table_ptr(other); } else if (Z_TYPE(intern->array) == IS_ARRAY) { - return Z_ARRVAL(intern->array); + return &Z_ARRVAL(intern->array); } else { zend_object *obj = Z_OBJ(intern->array); if (!obj->properties) { @@ -104,9 +104,22 @@ static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /* } obj->properties = zend_array_dup(obj->properties); } - return obj->properties; + return &obj->properties; } -} /* }}} */ +} +/* }}} */ + +static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /* {{{ */ + return *spl_array_get_hash_table_ptr(intern); +} +/* }}} */ + +static inline void spl_array_replace_hash_table(spl_array_object* intern, HashTable *ht) { /* {{{ */ + HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern); + zend_array_destroy(*ht_ptr); + *ht_ptr = ht; +} +/* }}} */ static inline zend_bool spl_array_is_object(spl_array_object *intern) /* {{{ */ { @@ -1432,16 +1445,12 @@ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fnam spl_array_object *intern = Z_SPLARRAY_P(getThis()); HashTable *aht = spl_array_get_hash_table(intern); zval function_name, params[2], *arg = NULL; - uint32_t old_refcount; ZVAL_STRINGL(&function_name, fname, fname_len); - /* A tricky way to pass "aht" by reference, reset refcount */ - //??? It may be not safe, if user comparison handler accesses "aht" - old_refcount = GC_REFCOUNT(aht); - GC_REFCOUNT(aht) = 1; ZVAL_NEW_EMPTY_REF(¶ms[0]); ZVAL_ARR(Z_REFVAL(params[0]), aht); + GC_REFCOUNT(aht)++; if (!use_arg) { intern->nApplyCount++; @@ -1470,10 +1479,16 @@ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fnam } exit: - /* A tricky way to pass "aht" by reference, copy back and cleanup */ - GC_REFCOUNT(aht) = old_refcount; - efree(Z_REF(params[0])); - zend_string_free(Z_STR(function_name)); + { + HashTable *new_ht = Z_ARRVAL_P(Z_REFVAL(params[0])); + if (aht != new_ht) { + spl_array_replace_hash_table(intern, new_ht); + } else { + GC_REFCOUNT(aht)--; + } + efree(Z_REF(params[0])); + zend_string_free(Z_STR(function_name)); + } } /* }}} */ #define SPL_ARRAY_METHOD(cname, fname, use_arg) \ diff --git a/ext/standard/array.c b/ext/standard/array.c index ccb46abe287..fe297ea79d7 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1029,38 +1029,31 @@ static int php_array_user_compare(const void *a, const void *b) /* {{{ */ static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, zend_bool renumber) /* {{{ */ { zval *array; - zend_refcounted *arr; + zend_array *arr; zend_bool retval; PHP_ARRAY_CMP_FUNC_VARS; PHP_ARRAY_CMP_FUNC_BACKUP(); - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/f", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { PHP_ARRAY_CMP_FUNC_RESTORE(); return; } - /* Increase reference counter, so the attempts to modify the array in user - * comparison function will create a copy of array and won't affect the - * original array. The fact of modification is detected by comparing the - * zend_array pointer. The result of sorting in such case is undefined and - * the function returns FALSE. - */ - Z_ADDREF_P(array); - arr = Z_COUNTED_P(array); - - retval = zend_hash_sort(Z_ARRVAL_P(array), compare_func, renumber) != FAILURE; - - if (arr != Z_COUNTED_P(array)) { - php_error_docref(NULL, E_WARNING, "Array was modified by the user comparison function"); - if (--GC_REFCOUNT(arr) <= 0) { - _zval_dtor_func(arr ZEND_FILE_LINE_CC); - } - retval = 0; - } else { - Z_DELREF_P(array); + arr = Z_ARR_P(array); + if (zend_hash_num_elements(arr) == 0) { + PHP_ARRAY_CMP_FUNC_RESTORE(); + RETURN_TRUE; } + /* Copy array, so the in-place modifications will not be visible to the callback function */ + arr = zend_array_dup(arr); + + retval = zend_hash_sort(arr, compare_func, renumber) != FAILURE; + + zval_ptr_dtor(array); + ZVAL_ARR(array, arr); + PHP_ARRAY_CMP_FUNC_RESTORE(); RETURN_BOOL(retval); } diff --git a/ext/standard/tests/array/bug71334.phpt b/ext/standard/tests/array/bug71334.phpt new file mode 100644 index 00000000000..7a37d0953a4 --- /dev/null +++ b/ext/standard/tests/array/bug71334.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #71334: Cannot access array keys while uksort() +--FILE-- + [1], + '-' => [2], + 'bar-test' => [3] + ]; + + private function _mySort($x, $y) + { + if (!isset($this->a[$x])) { + throw new Exception('Missing X: "' . $x . '"'); + } + + if (!isset($this->a[$y])) { + throw new Exception('Missing Y: "' . $y . '"'); + } + + return $x < $y; + } + + public function __construct() + { + uksort($this->a, [$this, '_mySort']); + } +} + +new myClass(); +echo "Done"; + +?> +--EXPECT-- +Done diff --git a/ext/standard/tests/array/unexpected_array_mod_bug.phpt b/ext/standard/tests/array/unexpected_array_mod_bug.phpt index 58f22492054..2762ebd8309 100644 --- a/ext/standard/tests/array/unexpected_array_mod_bug.phpt +++ b/ext/standard/tests/array/unexpected_array_mod_bug.phpt @@ -4,7 +4,7 @@ Crash when function parameter modified via reference $b; } $my_var = array(1 => "entry_1", 2 => "entry_2", @@ -12,10 +12,19 @@ $my_var = array(1 => "entry_1", 4 => "entry_4", 5 => "entry_5"); usort($my_var, "usercompare"); +var_dump($my_var); -echo "Done.\n"; ?> ---EXPECTF-- - -Warning: usort(): Array was modified by the user comparison function in %s on line %d -Done. +--EXPECT-- +array(5) { + [0]=> + string(7) "entry_1" + [1]=> + string(7) "entry_2" + [2]=> + string(7) "entry_3" + [3]=> + string(7) "entry_4" + [4]=> + string(7) "entry_5" +} diff --git a/ext/standard/tests/array/unexpected_array_mod_bug_variation1.phpt b/ext/standard/tests/array/unexpected_array_mod_bug_variation1.phpt new file mode 100644 index 00000000000..b5a1ee24d59 --- /dev/null +++ b/ext/standard/tests/array/unexpected_array_mod_bug_variation1.phpt @@ -0,0 +1,33 @@ +--TEST-- +Crash when function parameter modified via reference while keeping orig refcount +--FILE-- + "entry_1", + 2 => "entry_2", + 3 => "entry_3", + 4 => "entry_4", + 5 => "entry_5" +); +usort($array, function($a, $b) use (&$array, &$ref) { + unset($array[2]); + $ref = $array; + return $a <=> $b; +}); +var_dump($array); + +?> +--EXPECT-- +array(5) { + [0]=> + string(7) "entry_1" + [1]=> + string(7) "entry_2" + [2]=> + string(7) "entry_3" + [3]=> + string(7) "entry_4" + [4]=> + string(7) "entry_5" +} diff --git a/ext/standard/tests/file/file_put_contents_variation7.phpt b/ext/standard/tests/file/file_put_contents_variation7.phpt index 5c8e5f36023..b1b2face607 100644 --- a/ext/standard/tests/file/file_put_contents_variation7.phpt +++ b/ext/standard/tests/file/file_put_contents_variation7.phpt @@ -51,10 +51,10 @@ for($i = 0; $i $type ) { ?> ===DONE=== --EXPECTF-- + +Notice: A non well formed numeric value encountered in %s on line %d + +Notice: A non well formed numeric value encountered in %s on line %d + *** Testing floatval() on non floating types *** -- Iteration : -2147483648 -- @@ -151,4 +156,4 @@ float(0) -- Iteration : null -- float(0) -===DONE=== \ No newline at end of file +===DONE=== diff --git a/ext/standard/tests/general_functions/gettype_settype_variation2.phpt b/ext/standard/tests/general_functions/gettype_settype_variation2.phpt index 2242952769c..18b05b6a972 100644 --- a/ext/standard/tests/general_functions/gettype_settype_variation2.phpt +++ b/ext/standard/tests/general_functions/gettype_settype_variation2.phpt @@ -282,7 +282,7 @@ string(7) "integer" -- Iteration 18 -- string(6) "string" bool(true) -int(1) +int(100) string(7) "integer" -- Iteration 19 -- string(6) "string" @@ -297,7 +297,7 @@ string(7) "integer" -- Iteration 21 -- string(6) "string" bool(true) -int(-1) +int(0) string(7) "integer" -- Iteration 22 -- string(6) "string" @@ -312,7 +312,7 @@ string(7) "integer" -- Iteration 24 -- string(6) "string" bool(true) -int(1) +int(100) string(7) "integer" -- Iteration 25 -- string(6) "string" @@ -327,7 +327,7 @@ string(7) "integer" -- Iteration 27 -- string(6) "string" bool(true) -int(-1) +int(0) string(7) "integer" -- Iteration 28 -- string(6) "string" @@ -687,7 +687,7 @@ string(7) "integer" -- Iteration 18 -- string(6) "string" bool(true) -int(1) +int(100) string(7) "integer" -- Iteration 19 -- string(6) "string" @@ -702,7 +702,7 @@ string(7) "integer" -- Iteration 21 -- string(6) "string" bool(true) -int(-1) +int(0) string(7) "integer" -- Iteration 22 -- string(6) "string" @@ -717,7 +717,7 @@ string(7) "integer" -- Iteration 24 -- string(6) "string" bool(true) -int(1) +int(100) string(7) "integer" -- Iteration 25 -- string(6) "string" @@ -732,7 +732,7 @@ string(7) "integer" -- Iteration 27 -- string(6) "string" bool(true) -int(-1) +int(0) string(7) "integer" -- Iteration 28 -- string(6) "string" diff --git a/ext/standard/tests/math/decbin_basic.phpt b/ext/standard/tests/math/decbin_basic.phpt index b4389956f42..572a04245e9 100644 --- a/ext/standard/tests/math/decbin_basic.phpt +++ b/ext/standard/tests/math/decbin_basic.phpt @@ -31,7 +31,7 @@ string(2) "11" string(7) "1011111" string(4) "1010" string(12) "111101101110" -string(2) "11" +string(12) "111101101110" string(6) "100111" string(1) "0" string(1) "1" diff --git a/ext/standard/tests/math/dechex_basic.phpt b/ext/standard/tests/math/dechex_basic.phpt index 2423d8e748f..ac53a97b34f 100644 --- a/ext/standard/tests/math/dechex_basic.phpt +++ b/ext/standard/tests/math/dechex_basic.phpt @@ -30,7 +30,7 @@ string(1) "3" string(2) "5f" string(1) "a" string(3) "f6e" -string(1) "3" +string(3) "f6e" string(2) "27" string(1) "0" string(1) "1" diff --git a/ext/standard/tests/math/decoct_basic.phpt b/ext/standard/tests/math/decoct_basic.phpt index cc1f0a899ac..3a5011b973e 100644 --- a/ext/standard/tests/math/decoct_basic.phpt +++ b/ext/standard/tests/math/decoct_basic.phpt @@ -30,7 +30,7 @@ string(1) "3" string(3) "137" string(2) "12" string(4) "7556" -string(1) "3" +string(4) "7556" string(2) "47" string(1) "0" string(1) "1" diff --git a/ext/standard/tests/math/pow_variation1.phpt b/ext/standard/tests/math/pow_variation1.phpt index 5576e5b4933..c744c4eb9d4 100644 --- a/ext/standard/tests/math/pow_variation1.phpt +++ b/ext/standard/tests/math/pow_variation1.phpt @@ -143,21 +143,31 @@ int(1) int(0) -- Iteration 17 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 18 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 19 -- int(0) -- Iteration 20 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 21 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 22 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 23 -- diff --git a/ext/standard/tests/math/pow_variation1_64bit.phpt b/ext/standard/tests/math/pow_variation1_64bit.phpt index e1986ba8582..ea2ae45d18a 100644 --- a/ext/standard/tests/math/pow_variation1_64bit.phpt +++ b/ext/standard/tests/math/pow_variation1_64bit.phpt @@ -143,21 +143,31 @@ int(1) int(0) -- Iteration 17 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 18 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 19 -- int(0) -- Iteration 20 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 21 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 22 -- + +Warning: A non-numeric value encountered in %s on line %d int(0) -- Iteration 23 -- diff --git a/ext/standard/tests/math/pow_variation2.phpt b/ext/standard/tests/math/pow_variation2.phpt index f571936727a..36b085b6474 100644 --- a/ext/standard/tests/math/pow_variation2.phpt +++ b/ext/standard/tests/math/pow_variation2.phpt @@ -139,21 +139,31 @@ float(20.3) float(1) -- Iteration 17 -- + +Warning: A non-numeric value encountered in %s on line %d float(1) -- Iteration 18 -- + +Warning: A non-numeric value encountered in %s on line %d float(1) -- Iteration 19 -- int(1) -- Iteration 20 -- + +Warning: A non-numeric value encountered in %s on line %d float(1) -- Iteration 21 -- + +Warning: A non-numeric value encountered in %s on line %d float(1) -- Iteration 22 -- + +Warning: A non-numeric value encountered in %s on line %d float(1) -- Iteration 23 -- diff --git a/ext/standard/tests/strings/bug55871.phpt b/ext/standard/tests/strings/bug55871.phpt index 249d1bd3a32..0044f50ce70 100644 --- a/ext/standard/tests/strings/bug55871.phpt +++ b/ext/standard/tests/strings/bug55871.phpt @@ -41,6 +41,8 @@ array(1) { [0]=> string(0) "" } + +Warning: A non-numeric value encountered in %s on line %d array(1) { [0]=> string(40) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" diff --git a/ext/standard/type.c b/ext/standard/type.c index d6a412f4ec4..529d666d02d 100644 --- a/ext/standard/type.c +++ b/ext/standard/type.c @@ -154,7 +154,7 @@ PHP_FUNCTION(intval) ZEND_PARSE_PARAMETERS_END(); #endif - if (Z_TYPE_P(num) != IS_STRING) { + if (Z_TYPE_P(num) != IS_STRING || base == 10) { RETVAL_LONG(zval_get_long(num)); } else { RETVAL_LONG(ZEND_STRTOL(Z_STRVAL_P(num), NULL, base)); diff --git a/tests/lang/bug28800.phpt b/tests/lang/bug28800.phpt index f81ad7fec98..8bd2c306e1b 100644 --- a/tests/lang/bug28800.phpt +++ b/tests/lang/bug28800.phpt @@ -7,11 +7,23 @@ Bug #28800 (Incorrect string to number conversion for strings starting with 'inf echo ($v+0)."\n"; } ?> ---EXPECT-- -0 -0 -0 -0 -0 +--EXPECTF-- + +Warning: A non-numeric value encountered in %s on line %d +0 + +Warning: A non-numeric value encountered in %s on line %d +0 + +Warning: A non-numeric value encountered in %s on line %d +0 + +Warning: A non-numeric value encountered in %s on line %d +0 + +Warning: A non-numeric value encountered in %s on line %d +0 + +Warning: A non-numeric value encountered in %s on line %d 0 diff --git a/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt b/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt index d5888d837f9..69fd90f1c8c 100644 --- a/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt +++ b/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt @@ -226,17 +226,17 @@ int(984) --- testing: '123abc' << 'a5.9' --- int(123) --- testing: '123e5' << '0' --- -int(123) +int(12300000) --- testing: '123e5' << '65' --- int(0) --- testing: '123e5' << '-44' --- Exception: Bit shift by negative number --- testing: '123e5' << '1.2' --- -int(246) +int(24600000) --- testing: '123e5' << '-7.7' --- Exception: Bit shift by negative number --- testing: '123e5' << 'abc' --- -int(123) +int(12300000) --- testing: '123e5' << '123abc' --- int(0) --- testing: '123e5' << '123e5' --- @@ -250,21 +250,21 @@ int(0) --- testing: '123e5' << '123abc ' --- int(0) --- testing: '123e5' << '3.4a' --- -int(984) +int(98400000) --- testing: '123e5' << 'a5.9' --- -int(123) +int(12300000) --- testing: '123e5xyz' << '0' --- -int(123) +int(12300000) --- testing: '123e5xyz' << '65' --- int(0) --- testing: '123e5xyz' << '-44' --- Exception: Bit shift by negative number --- testing: '123e5xyz' << '1.2' --- -int(246) +int(24600000) --- testing: '123e5xyz' << '-7.7' --- Exception: Bit shift by negative number --- testing: '123e5xyz' << 'abc' --- -int(123) +int(12300000) --- testing: '123e5xyz' << '123abc' --- int(0) --- testing: '123e5xyz' << '123e5' --- @@ -278,9 +278,9 @@ int(0) --- testing: '123e5xyz' << '123abc ' --- int(0) --- testing: '123e5xyz' << '3.4a' --- -int(984) +int(98400000) --- testing: '123e5xyz' << 'a5.9' --- -int(123) +int(12300000) --- testing: ' 123abc' << '0' --- int(123) --- testing: ' 123abc' << '65' --- diff --git a/tests/lang/operators/bitwiseShiftRight_variationStr.phpt b/tests/lang/operators/bitwiseShiftRight_variationStr.phpt index a86d0cfddb2..a4c425aab3e 100644 --- a/tests/lang/operators/bitwiseShiftRight_variationStr.phpt +++ b/tests/lang/operators/bitwiseShiftRight_variationStr.phpt @@ -222,17 +222,17 @@ int(15) --- testing: '123abc' >> 'a5.9' --- int(123) --- testing: '123e5' >> '0' --- -int(123) +int(12300000) --- testing: '123e5' >> '65' --- int(0) --- testing: '123e5' >> '-44' --- Exception: Bit shift by negative number --- testing: '123e5' >> '1.2' --- -int(61) +int(6150000) --- testing: '123e5' >> '-7.7' --- Exception: Bit shift by negative number --- testing: '123e5' >> 'abc' --- -int(123) +int(12300000) --- testing: '123e5' >> '123abc' --- int(0) --- testing: '123e5' >> '123e5' --- @@ -246,21 +246,21 @@ int(0) --- testing: '123e5' >> '123abc ' --- int(0) --- testing: '123e5' >> '3.4a' --- -int(15) +int(1537500) --- testing: '123e5' >> 'a5.9' --- -int(123) +int(12300000) --- testing: '123e5xyz' >> '0' --- -int(123) +int(12300000) --- testing: '123e5xyz' >> '65' --- int(0) --- testing: '123e5xyz' >> '-44' --- Exception: Bit shift by negative number --- testing: '123e5xyz' >> '1.2' --- -int(61) +int(6150000) --- testing: '123e5xyz' >> '-7.7' --- Exception: Bit shift by negative number --- testing: '123e5xyz' >> 'abc' --- -int(123) +int(12300000) --- testing: '123e5xyz' >> '123abc' --- int(0) --- testing: '123e5xyz' >> '123e5' --- @@ -274,9 +274,9 @@ int(0) --- testing: '123e5xyz' >> '123abc ' --- int(0) --- testing: '123e5xyz' >> '3.4a' --- -int(15) +int(1537500) --- testing: '123e5xyz' >> 'a5.9' --- -int(123) +int(12300000) --- testing: ' 123abc' >> '0' --- int(123) --- testing: ' 123abc' >> '65' --- diff --git a/tests/lang/operators/modulus_variationStr.phpt b/tests/lang/operators/modulus_variationStr.phpt index c647ecd3805..4cfd7768ffe 100644 --- a/tests/lang/operators/modulus_variationStr.phpt +++ b/tests/lang/operators/modulus_variationStr.phpt @@ -208,9 +208,9 @@ Exception: Modulo by zero --- testing: '123abc' % '123abc' --- int(0) --- testing: '123abc' % '123e5' --- -int(0) +int(123) --- testing: '123abc' % '123e5xyz' --- -int(0) +int(123) --- testing: '123abc' % ' 123abc' --- int(0) --- testing: '123abc' % '123 abc' --- @@ -224,13 +224,13 @@ Exception: Modulo by zero --- testing: '123e5' % '0' --- Exception: Modulo by zero --- testing: '123e5' % '65' --- -int(58) +int(50) --- testing: '123e5' % '-44' --- -int(35) +int(20) --- testing: '123e5' % '1.2' --- int(0) --- testing: '123e5' % '-7.7' --- -int(4) +int(6) --- testing: '123e5' % 'abc' --- Exception: Modulo by zero --- testing: '123e5' % '123abc' --- @@ -252,13 +252,13 @@ Exception: Modulo by zero --- testing: '123e5xyz' % '0' --- Exception: Modulo by zero --- testing: '123e5xyz' % '65' --- -int(58) +int(50) --- testing: '123e5xyz' % '-44' --- -int(35) +int(20) --- testing: '123e5xyz' % '1.2' --- int(0) --- testing: '123e5xyz' % '-7.7' --- -int(4) +int(6) --- testing: '123e5xyz' % 'abc' --- Exception: Modulo by zero --- testing: '123e5xyz' % '123abc' --- @@ -292,9 +292,9 @@ Exception: Modulo by zero --- testing: ' 123abc' % '123abc' --- int(0) --- testing: ' 123abc' % '123e5' --- -int(0) +int(123) --- testing: ' 123abc' % '123e5xyz' --- -int(0) +int(123) --- testing: ' 123abc' % ' 123abc' --- int(0) --- testing: ' 123abc' % '123 abc' --- @@ -320,9 +320,9 @@ Exception: Modulo by zero --- testing: '123 abc' % '123abc' --- int(0) --- testing: '123 abc' % '123e5' --- -int(0) +int(123) --- testing: '123 abc' % '123e5xyz' --- -int(0) +int(123) --- testing: '123 abc' % ' 123abc' --- int(0) --- testing: '123 abc' % '123 abc' --- @@ -348,9 +348,9 @@ Exception: Modulo by zero --- testing: '123abc ' % '123abc' --- int(0) --- testing: '123abc ' % '123e5' --- -int(0) +int(123) --- testing: '123abc ' % '123e5xyz' --- -int(0) +int(123) --- testing: '123abc ' % ' 123abc' --- int(0) --- testing: '123abc ' % '123 abc' --- diff --git a/tests/lang/operators/negate_variationStr.phpt b/tests/lang/operators/negate_variationStr.phpt index a25bdda7f59..7405d42882e 100644 --- a/tests/lang/operators/negate_variationStr.phpt +++ b/tests/lang/operators/negate_variationStr.phpt @@ -16,7 +16,7 @@ foreach ($strVals as $strVal) { ?> ===DONE=== ---EXPECT-- +--EXPECTF-- --- testing: '0' --- int(0) --- testing: '65' --- @@ -28,21 +28,37 @@ float(-1.2) --- testing: '-7.7' --- float(7.7) --- testing: 'abc' --- + +Warning: A non-numeric value encountered in %s on line %d int(0) --- testing: '123abc' --- + +Notice: A non well formed numeric value encountered in %s on line %d int(-123) --- testing: '123e5' --- float(-12300000) --- testing: '123e5xyz' --- + +Notice: A non well formed numeric value encountered in %s on line %d float(-12300000) --- testing: ' 123abc' --- + +Notice: A non well formed numeric value encountered in %s on line %d int(-123) --- testing: '123 abc' --- + +Notice: A non well formed numeric value encountered in %s on line %d int(-123) --- testing: '123abc ' --- + +Notice: A non well formed numeric value encountered in %s on line %d int(-123) --- testing: '3.4a' --- + +Notice: A non well formed numeric value encountered in %s on line %d float(-3.4) --- testing: 'a5.9' --- + +Warning: A non-numeric value encountered in %s on line %d int(0) ===DONE===