Skip to content

Commit

Permalink
MDEV-33534 UBSAN: Negation of -X cannot be represented in type 'long …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
abarkov committed Apr 27, 2024
1 parent 3d41747 commit 3141a68
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 10 deletions.
15 changes: 15 additions & 0 deletions mysql-test/main/func_math.result
Original file line number Diff line number Diff line change
Expand Up @@ -3617,5 +3617,20 @@ SELECT CRC32(ExtractValue('<a><b/></a>', '/a/b')) AS f;
f
0
#
# 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|
#
SELECT TRUNCATE(EXP(-1.e-2),-1.e+30) AS c1;
c1
0
SELECT (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) AS c1;
c1
0
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;
c1
0
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;
c1
NULL
#
# End of 10.5 tests
#
10 changes: 10 additions & 0 deletions mysql-test/main/func_math.test
Original file line number Diff line number Diff line change
Expand Up @@ -1929,6 +1929,16 @@ DROP TABLE t2, t1;
SELECT CRC32(ExtractValue('<a><b/></a>', '/a/b')) AS f;


--echo #
--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|
--echo #

SELECT TRUNCATE(EXP(-1.e-2),-1.e+30) AS c1;
SELECT (TRUNCATE(EXP(-1.e-2),-1.e+30) % RADIANS(-1)) AS c1;
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;
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;


--echo #
--echo # End of 10.5 tests
--echo #
22 changes: 12 additions & 10 deletions sql/item_func.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2638,12 +2638,12 @@ void Item_func_round::fix_arg_hex_hybrid()
}


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

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

if (dec_negative && std::isinf(tmp))
tmp2= 0.0;
else if (!dec_negative && std::isinf(value_mul_tmp))
if (dec.neg() && std::isinf(tmp)) // "dec" is a too small negative number
return 0.0;

// Handle "dec" with a reasonably small absolute value
if (!dec.neg() && std::isinf(value_mul_tmp))
tmp2= value;
else if (truncate)
{
if (value >= 0.0)
tmp2= dec < 0 ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp;
tmp2= dec.neg() ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp;
else
tmp2= dec < 0 ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp;
tmp2= dec.neg() ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp;
}
else
tmp2=dec < 0 ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp;
tmp2=dec.neg() ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp;

return tmp2;
}
Expand Down

0 comments on commit 3141a68

Please sign in to comment.