integer overflow in mini-gmp due to integer promotion

Vincent Lefevre vincent at vinc17.net
Wed Jul 19 15:45:26 CEST 2023


Hi,

When I test MPFR with

./configure --with-mini-gmp=/home/vlefevre/software/gmp/mini-gmp CC=gcc-13 CFLAGS="-O2 -fsanitize=undefined -fno-sanitize-recover -DMINI_GMP_LIMB_TYPE=short"

I get lots of failures in mini-gmp.c (I suspect that the errors were
hidden by some optimization in GCC 12 and before).

For instance:

cventin:...ftware/mpfr/tests> ./texceptions
mini-gmp.c:993:7: runtime error: signed integer overflow: 46604 * 61440 cannot be represented in type 'int'

This is a call to gmp_udiv_qrnnd_preinv, which has

#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di)                      \
  do {                                                                  \
    mp_limb_t _qh, _ql, _r, _mask;                                      \
    gmp_umul_ppmm (_qh, _ql, (nh), (di));                               \
    gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl));                \
    _r = (nl) - _qh * (d);                                              \
[...]

Here, both _qh and d have type mp_limb_t, which is an unsigned short.
Since this is a 16-bit type and int is on 32 bits, unsigned short is
promoted to int, which is now a signed integer type, hence the error.

Ditto in gmp_udiv_qr_3by2.

I suppose that the simplest solution would be to add a cast to
unsigned long, so that the multiplication is done in this type (and
the subtraction too as a consequence), as this should be the largest
MINI_GMP_LIMB_TYPE size... hoping that the compiler will optimize.

I've attached a patch.

On the following testcase, GCC generates the same code for i1 and i2
and for s1 and s2 (tested on 32-bit and 64-bit x86, on aarch64, and
on ppc64le):

unsigned int i1 (unsigned int a, unsigned int b, unsigned int c)
{
  return a - b * c;
}

unsigned int i2 (unsigned int a, unsigned int b, unsigned int c)
{
  return a - (unsigned long) b * c;
}

unsigned short s1 (unsigned short a, unsigned short b, unsigned short c)
{
  return a - b * c;
}

unsigned short s2 (unsigned short a, unsigned short b, unsigned short c)
{
  return a - (unsigned long) b * c;
}

Otherwise the sizes of the types could be checked like in
gmp_umul_ppmm.

-- 
Vincent Lefèvre <vincent at vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: mini-gmp-intoverflow.patch
Type: text/x-diff
Size: 956 bytes
Desc: not available
URL: <https://gmplib.org/list-archives/gmp-bugs/attachments/20230719/80b36556/attachment.bin>


More information about the gmp-bugs mailing list