Browse Source

MDEV-22742 UBSAN: Many overflow issues in strings/decimal.c - runtime error: signed integer overflow: x * y cannot be represented in type 'long long int' (on optimized builds).

Avoid integer overflow, do the check before the calculation.
pull/2063/head
Alexey Botchkov 4 years ago
parent
commit
6277e7df6b
  1. 28
      strings/decimal.c

28
strings/decimal.c

@ -1128,13 +1128,21 @@ int decimal2ulonglong(const decimal_t *from, ulonglong *to)
for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1) for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1)
{ {
ulonglong y=x;
x=x*DIG_BASE + *buf++;
if (unlikely(y > ((ulonglong) ULONGLONG_MAX/DIG_BASE) || x < y))
/*
Check that the decimal is bigger than any possible integer.
Do it before we do the x*=DIB_BASE to avoid integer
overflow.
*/
if (unlikely (
x >= ULONGLONG_MAX/DIG_BASE &&
(x > ULONGLONG_MAX/DIG_BASE ||
*buf > (dec1) (ULONGLONG_MAX%DIG_BASE))))
{ {
*to=ULONGLONG_MAX; *to=ULONGLONG_MAX;
return E_DEC_OVERFLOW; return E_DEC_OVERFLOW;
} }
x=x*DIG_BASE + *buf++;
} }
*to=x; *to=x;
for (frac=from->frac; unlikely(frac > 0); frac-=DIG_PER_DEC1) for (frac=from->frac; unlikely(frac > 0); frac-=DIG_PER_DEC1)
@ -1151,15 +1159,19 @@ int decimal2longlong(const decimal_t *from, longlong *to)
for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1) for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1)
{ {
longlong y=x;
/* /*
Check that the decimal is less than any possible integer.
Do it before we do the x*=DIB_BASE to avoid integer
overflow.
Attention: trick! Attention: trick!
we're calculating -|from| instead of |from| here we're calculating -|from| instead of |from| here
because |LONGLONG_MIN| > LONGLONG_MAX because |LONGLONG_MIN| > LONGLONG_MAX
so we can convert -9223372036854775808 correctly
so we can convert -9223372036854775808 correctly.
*/ */
x=x*DIG_BASE - *buf++;
if (unlikely(y < (LONGLONG_MIN/DIG_BASE) || x > y))
if (unlikely (
x <= LONGLONG_MIN/DIG_BASE &&
(x < LONGLONG_MIN/DIG_BASE ||
*buf > (dec1) (-(LONGLONG_MIN%DIG_BASE)))))
{ {
/* /*
the decimal is bigger than any possible integer the decimal is bigger than any possible integer
@ -1168,6 +1180,8 @@ int decimal2longlong(const decimal_t *from, longlong *to)
*to= from->sign ? LONGLONG_MIN : LONGLONG_MAX; *to= from->sign ? LONGLONG_MIN : LONGLONG_MAX;
return E_DEC_OVERFLOW; return E_DEC_OVERFLOW;
} }
x=x*DIG_BASE - *buf++;
} }
/* boundary case: 9223372036854775808 */ /* boundary case: 9223372036854775808 */
if (unlikely(from->sign==0 && x == LONGLONG_MIN)) if (unlikely(from->sign==0 && x == LONGLONG_MIN))

Loading…
Cancel
Save