Skip to content

Commit 3141a68

Browse files
committed
MDEV-33534 UBSAN: Negation of -X cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself in my_double_round from sql/item_func.cc|
The negation in this line: ulonglong abs_dec= dec_negative ? -dec : dec; did not take into account that 'dec' can be the smallest possible signed negative value -9223372036854775808. Its negation is an operation with an undefined behavior. Fixing the code to use Longlong_hybrid, which implements a safe method to get an absolute value.
1 parent 3d41747 commit 3141a68

File tree

3 files changed

+37
-10
lines changed

3 files changed

+37
-10
lines changed

mysql-test/main/func_math.result

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3617,5 +3617,20 @@ SELECT CRC32(ExtractValue('<a><b/></a>', '/a/b')) AS f;
36173617
f
36183618
0
36193619
#
3620+
# MDEV-33534 UBSAN: Negation of -X cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself in my_double_round from sql/item_func.cc|
3621+
#
3622+
SELECT TRUNCATE(EXP(-1.e-2),-1.e+30) AS c1;
3623+
c1
3624+
0
3625+
SELECT (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) AS c1;
3626+
c1
3627+
0
3628+
SELECT (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) * (LAST_DAY('1-03-30 1:29:12') MOD 1 + COS(-1)) AS c1;
3629+
c1
3630+
0
3631+
SELECT(ASIN(-1)+ LN(-1)) % (ATAN(-1) MOD FLOOR(1)) * (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) * (LAST_DAY('1-03-30 1:29:12') MOD 1 + COS(-1)) AS c1;
3632+
c1
3633+
NULL
3634+
#
36203635
# End of 10.5 tests
36213636
#

mysql-test/main/func_math.test

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,16 @@ DROP TABLE t2, t1;
19291929
SELECT CRC32(ExtractValue('<a><b/></a>', '/a/b')) AS f;
19301930

19311931

1932+
--echo #
1933+
--echo # MDEV-33534 UBSAN: Negation of -X cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself in my_double_round from sql/item_func.cc|
1934+
--echo #
1935+
1936+
SELECT TRUNCATE(EXP(-1.e-2),-1.e+30) AS c1;
1937+
SELECT (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) AS c1;
1938+
SELECT (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) * (LAST_DAY('1-03-30 1:29:12') MOD 1 + COS(-1)) AS c1;
1939+
SELECT(ASIN(-1)+ LN(-1)) % (ATAN(-1) MOD FLOOR(1)) * (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) * (LAST_DAY('1-03-30 1:29:12') MOD 1 + COS(-1)) AS c1;
1940+
1941+
19321942
--echo #
19331943
--echo # End of 10.5 tests
19341944
--echo #

sql/item_func.cc

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,12 +2638,12 @@ void Item_func_round::fix_arg_hex_hybrid()
26382638
}
26392639

26402640

2641-
double my_double_round(double value, longlong dec, bool dec_unsigned,
2641+
double my_double_round(double value, longlong dec_value, bool dec_unsigned,
26422642
bool truncate)
26432643
{
26442644
double tmp;
2645-
bool dec_negative= (dec < 0) && !dec_unsigned;
2646-
ulonglong abs_dec= dec_negative ? -dec : dec;
2645+
const Longlong_hybrid dec(dec_value, dec_unsigned);
2646+
const ulonglong abs_dec= dec.abs();
26472647
/*
26482648
tmp2 is here to avoid return the value with 80 bit precision
26492649
This will fix that the test round(0.1,1) = round(0.1,1) is true
@@ -2658,22 +2658,24 @@ double my_double_round(double value, longlong dec, bool dec_unsigned,
26582658
volatile double value_div_tmp= value / tmp;
26592659
volatile double value_mul_tmp= value * tmp;
26602660

2661-
if (!dec_negative && std::isinf(tmp)) // "dec" is too large positive number
2661+
if (!dec.neg() && std::isinf(tmp)) // "dec" is a too large positive number
26622662
return value;
26632663

2664-
if (dec_negative && std::isinf(tmp))
2665-
tmp2= 0.0;
2666-
else if (!dec_negative && std::isinf(value_mul_tmp))
2664+
if (dec.neg() && std::isinf(tmp)) // "dec" is a too small negative number
2665+
return 0.0;
2666+
2667+
// Handle "dec" with a reasonably small absolute value
2668+
if (!dec.neg() && std::isinf(value_mul_tmp))
26672669
tmp2= value;
26682670
else if (truncate)
26692671
{
26702672
if (value >= 0.0)
2671-
tmp2= dec < 0 ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp;
2673+
tmp2= dec.neg() ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp;
26722674
else
2673-
tmp2= dec < 0 ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp;
2675+
tmp2= dec.neg() ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp;
26742676
}
26752677
else
2676-
tmp2=dec < 0 ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp;
2678+
tmp2=dec.neg() ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp;
26772679

26782680
return tmp2;
26792681
}

0 commit comments

Comments
 (0)