From 5df71d977559b22edf929948af3df957e0e3ffb5 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Wed, 29 Jul 2026 18:00:32 +0000 Subject: [PATCH] ext/standard: Warn when number base conversion loses precision _php_math_basetozval is used for base_convert, bindec, hexdec and octdec. It uses an integer or double for the internal representation of the number. When the input number is too large to fit in there, it loses precision. This commit emits a notice when this happens. Discussion thread: https://news-web.php.net/php.internals/131364 PR: https://github.com/php/php-src/pull/22371 --- NEWS | 2 ++ .../zend_ini/zend_ini_parse_uquantity_overflow.phpt | 4 ++-- ext/standard/math.c | 1 + ext/standard/tests/math/base_convert_overflow.phpt | 10 ++++++++++ ext/standard/tests/math/bindec_basic.phpt | 2 ++ ext/standard/tests/math/bindec_basiclong_64bit.phpt | 8 +++++++- ext/standard/tests/math/gh22395.phpt | 3 ++- ext/standard/tests/math/hexdec.phpt | 12 ++++++++---- ext/standard/tests/math/hexdec_basic.phpt | 6 ++++++ ext/standard/tests/math/hexdec_basiclong_64bit.phpt | 8 +++++++- ext/standard/tests/math/hexdec_variation1.phpt | 8 ++++++++ ext/standard/tests/math/octdec_basic.phpt | 2 ++ ext/standard/tests/math/octdec_basiclong_64bit.phpt | 8 +++++++- 13 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 ext/standard/tests/math/base_convert_overflow.phpt diff --git a/NEWS b/NEWS index c144b3c62c23..cb70bda237ff 100644 --- a/NEWS +++ b/NEWS @@ -276,6 +276,8 @@ PHP NEWS (David Carlier) . TSRM: make CG, EG, SCNG and AG compile-time offsets. (henderkes) . Deprecate returning values from __construct() and __destruct(). (timwolla) + . base_convert, bindex, hexdec and octdec now raise a notice when they cannot + precisely convert the given number. (Sjoerd Langkemper) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt b/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt index 0da9c4fac976..f770ccbcaf20 100644 --- a/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt +++ b/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt @@ -15,8 +15,8 @@ $tests = [ 'No overflow 007' => ' -1', 'No overflow 008' => '-1 ', 'No overflow 009' => ' -1 ', - 'Subject overflow 001' => base_convert(str_repeat('1', PHP_INT_SIZE*8+1), 2, 10), - 'Subject overflow 002' => '-'.base_convert(str_repeat('1', PHP_INT_SIZE*8+1), 2, 10), + 'Subject overflow 001' => PHP_INT_MAX.'0', + 'Subject overflow 002' => PHP_INT_MIN.'0', 'Subject overflow 003' => strval(PHP_INT_MIN), 'Subject overflow 004' => '-2', 'Subject overflow 005' => '-1K', diff --git a/ext/standard/math.c b/ext/standard/math.c index 1498a1efc0b2..e13f153f63e2 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -899,6 +899,7 @@ PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret) num = num * base + c; break; } else { + zend_error(E_NOTICE, "Input number is larger than PHP_INT_MAX, precision has been lost in conversion"); fnum = (double)num; mode = 1; } diff --git a/ext/standard/tests/math/base_convert_overflow.phpt b/ext/standard/tests/math/base_convert_overflow.phpt new file mode 100644 index 000000000000..0f74bdc76a63 --- /dev/null +++ b/ext/standard/tests/math/base_convert_overflow.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test base_convert() - overflow test +--FILE-- + +--EXPECTF-- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d +string(18) "2ee03c32ad644bd1e1" \ No newline at end of file diff --git a/ext/standard/tests/math/bindec_basic.phpt b/ext/standard/tests/math/bindec_basic.phpt index 7640171fe61b..d1197ed1645c 100644 --- a/ext/standard/tests/math/bindec_basic.phpt +++ b/ext/standard/tests/math/bindec_basic.phpt @@ -50,6 +50,8 @@ int(129) int(455) int(224) int(2147483647) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2147483648) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d diff --git a/ext/standard/tests/math/bindec_basiclong_64bit.phpt b/ext/standard/tests/math/bindec_basiclong_64bit.phpt index b4f28a091dbb..eb648c7c535f 100644 --- a/ext/standard/tests/math/bindec_basiclong_64bit.phpt +++ b/ext/standard/tests/math/bindec_basiclong_64bit.phpt @@ -30,18 +30,24 @@ foreach ($binLongStrs as $strVal) { } ?> ---EXPECT-- +--EXPECTF-- --- testing: 0111111111111111111111111111111111111111111111111111111111111111 --- int(9223372036854775807) --- testing: 1111111111111111111111111111111111111111111111111111111111111111 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 01111111111111111111111111111111 --- int(2147483647) --- testing: 11111111111111111111111111111111 --- int(4294967295) --- testing: 01111111111111111111111111111111111111111111111111111111111111111 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 11111111111111111111111111111111111111111111111111111111111111111 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(3.6893488147419103E+19) --- testing: 011111111111111111111111111111111 --- int(4294967295) diff --git a/ext/standard/tests/math/gh22395.phpt b/ext/standard/tests/math/gh22395.phpt index 73c2c66da199..6ccbb44d669f 100644 --- a/ext/standard/tests/math/gh22395.phpt +++ b/ext/standard/tests/math/gh22395.phpt @@ -6,6 +6,7 @@ $result = base_convert(str_repeat("1", 61), 36, 16); var_dump(strlen($result)); var_dump(substr($result, 0, 13)); ?> ---EXPECT-- +--EXPECTF-- +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d int(78) string(13) "4b61b5e0639ff" diff --git a/ext/standard/tests/math/hexdec.phpt b/ext/standard/tests/math/hexdec.phpt index ba09875efc39..1f8b57d83fd0 100644 --- a/ext/standard/tests/math/hexdec.phpt +++ b/ext/standard/tests/math/hexdec.phpt @@ -10,8 +10,8 @@ var_dump(hexdec("12345")); var_dump(hexdec("q12345")); var_dump(hexdec("12345+?!")); var_dump(hexdec("12345q")); -var_dump((float)hexdec("1234500001")); -var_dump((float)hexdec("17fffffff")); +var_dump(hexdec("12345678901234567")); +var_dump(hexdec("17fffffffffffffff")); ?> --EXPECTF-- @@ -26,5 +26,9 @@ int(74565) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d int(74565) -float(78187069441) -float(6442450943) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d +float(2.0988295476557332E+19) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d +float(2.7670116110564327E+19) diff --git a/ext/standard/tests/math/hexdec_basic.phpt b/ext/standard/tests/math/hexdec_basic.phpt index 2f06b9650aed..f1fd76778bfd 100644 --- a/ext/standard/tests/math/hexdec_basic.phpt +++ b/ext/standard/tests/math/hexdec_basic.phpt @@ -32,11 +32,17 @@ for ($i = 0; $i < count($values); $i++) { --EXPECTF-- int(18433668) int(126895953) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(142929835591) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(142929835592) int(1194684) int(7904751) int(2147483647) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2147483648) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d diff --git a/ext/standard/tests/math/hexdec_basiclong_64bit.phpt b/ext/standard/tests/math/hexdec_basiclong_64bit.phpt index af0b448cac01..7475d982004c 100644 --- a/ext/standard/tests/math/hexdec_basiclong_64bit.phpt +++ b/ext/standard/tests/math/hexdec_basiclong_64bit.phpt @@ -30,18 +30,24 @@ foreach ($hexLongStrs as $strVal) { } ?> ---EXPECT-- +--EXPECTF-- --- testing: 7fffffffffffffff --- int(9223372036854775807) --- testing: ffffffffffffffff --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 7fffffff --- int(2147483647) --- testing: ffffffff --- int(4294967295) --- testing: 7ffffffffffffffff --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.4757395258967641E+20) --- testing: ffffffffffffffffff --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(4.722366482869645E+21) --- testing: 7ffffffff --- int(34359738367) diff --git a/ext/standard/tests/math/hexdec_variation1.phpt b/ext/standard/tests/math/hexdec_variation1.phpt index 65ebbe347b9b..cf0d4e81411c 100644 --- a/ext/standard/tests/math/hexdec_variation1.phpt +++ b/ext/standard/tests/math/hexdec_variation1.phpt @@ -86,9 +86,13 @@ Deprecated: Invalid characters passed for attempted conversion, these have been int(9029) -- Iteration 5 -- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(285960729237) -- Iteration 6 -- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(285960729238) -- Iteration 7 -- @@ -102,10 +106,14 @@ Deprecated: Invalid characters passed for attempted conversion, these have been int(261) -- Iteration 9 -- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(20015998341120) -- Iteration 10 -- +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d + Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d float(1250999896553) diff --git a/ext/standard/tests/math/octdec_basic.phpt b/ext/standard/tests/math/octdec_basic.phpt index 4c3ffc8b8a91..935cb23e1d35 100644 --- a/ext/standard/tests/math/octdec_basic.phpt +++ b/ext/standard/tests/math/octdec_basic.phpt @@ -46,6 +46,8 @@ int(5349) int(342391) int(375) int(2147483647) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2147483648) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d diff --git a/ext/standard/tests/math/octdec_basiclong_64bit.phpt b/ext/standard/tests/math/octdec_basiclong_64bit.phpt index 06e0dd3929d2..4637893d3470 100644 --- a/ext/standard/tests/math/octdec_basiclong_64bit.phpt +++ b/ext/standard/tests/math/octdec_basiclong_64bit.phpt @@ -30,18 +30,24 @@ foreach ($octLongStrs as $strVal) { } ?> ---EXPECT-- +--EXPECTF-- --- testing: 777777777777777777777 --- int(9223372036854775807) --- testing: 1777777777777777777777 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 17777777777 --- int(2147483647) --- testing: 37777777777 --- int(4294967295) --- testing: 377777777777777777777777 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2.3611832414348226E+21) --- testing: 17777777777777777777777777 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(7.555786372591432E+22) --- testing: 377777777777 --- int(34359738367)