[Gmp-commit] /var/hg/gmp: Avoid signed overflow (undefined behaviour) in mini...

mercurial at gmplib.org mercurial at gmplib.org
Sun Nov 20 21:09:21 UTC 2016


details:   /var/hg/gmp/rev/6a2a9d2f639c
changeset: 17120:6a2a9d2f639c
user:      Niels M?ller <nisse at lysator.liu.se>
date:      Sun Nov 20 22:09:04 2016 +0100
description:
Avoid signed overflow (undefined behaviour) in mini-gmp/tests/t-signed.

diffstat:

 mini-gmp/ChangeLog        |    5 +
 mini-gmp/tests/t-signed.c |  188 +++++++++++++++++++++++++++++----------------
 2 files changed, 124 insertions(+), 69 deletions(-)

diffs (261 lines):

diff -r 2e2072f55b3f -r 6a2a9d2f639c mini-gmp/ChangeLog
--- a/mini-gmp/ChangeLog	Sat Nov 19 21:23:06 2016 +0100
+++ b/mini-gmp/ChangeLog	Sun Nov 20 22:09:04 2016 +0100
@@ -1,3 +1,8 @@
+2016-11-20  Niels Möller  <nisse at lysator.liu.se>
+
+	* tests/t-signed.c: Reorganize testcase, to avoid undefined
+	behaviour with signed overflow.
+
 2016-11-19  Niels Möller  <nisse at lysator.liu.se>
 
 	* tests/run-tests: Set up LD_LIBRARY_PATH and DYLD_LIBRARY_PATH
diff -r 2e2072f55b3f -r 6a2a9d2f639c mini-gmp/tests/t-signed.c
--- a/mini-gmp/tests/t-signed.c	Sat Nov 19 21:23:06 2016 +0100
+++ b/mini-gmp/tests/t-signed.c	Sun Nov 20 22:09:04 2016 +0100
@@ -17,55 +17,83 @@
 You should have received a copy of the GNU General Public License along with
 the GNU MP Library test suite.  If not, see https://www.gnu.org/licenses/.  */
 
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "testutils.h"
 
+/* Always called with sz fitting in a signed long, and si is the
+   corresponding value. */ 
 int
-check_si (mpz_t sz, mpz_t oz, long si, long oi, int c)
+check_si (const mpz_t sz, long si)
 {
   mpz_t t;
-  int fail;
 
-  if (mpz_cmp_si (sz, oi) != c)
+  /* Checks on sz/si */
+  if ((mpz_cmp_si (sz, si)) != 0)
     {
-      printf ("mpz_cmp_si (sz, %ld) != %i.\n", oi, c);
-      printf (" sz="); mpz_out_str (stdout, 10, sz); printf ("\n");
-      abort ();
+      printf ("mpz_cmp_si (sz, %ld) != 0.\n", si);
+      return 0;
     }
-
-  if ((si < oi ? -1 : si > oi) != c)
-    return 1;
+  if (mpz_get_si (sz) != si)
+    {
+      printf ("mpz_get_si (sz) != %ld.\n", si);
+      return 0;
+    }
 
   mpz_init_set_si (t, si);
 
-  if ((fail = mpz_cmp_si (sz, si)) != 0)
-    printf ("mpz_cmp_si (sz, %ld) != 0.\n", si);
-  if (mpz_cmp_si (oz, si) != -c)
-    printf ("mpz_cmp_si (oz, %ld) != %i.\n", si, -c), fail = 1;
-  if (! mpz_fits_slong_p (sz))
-    printf ("mpz_fits_slong_p (sz) != 1.\n"), fail = 1;
-  if (mpz_get_si (sz) != si)
-    printf ("mpz_get_si (sz) != %ld.\n", si), fail = 1;
   if (mpz_cmp (t, sz) != 0)
     {
       printf ("mpz_init_set_si (%ld) failed.\n", si);
       printf (" got="); mpz_out_str (stdout, 10, t); printf ("\n");
-      fail = 1;
+      return 0;
     }
 
   mpz_clear (t);
+  return 1;
+}
 
-  if (fail)
+/* Called with mpz_cmp (sz, oz) == c. If sz fits in a signed long,
+   si is the coresponding value, and similarly for oz and oi. */ 
+void
+check_si_cmp (const mpz_t sz, const mpz_t oz, long si, long oi, int c)
+{
+  if (mpz_cmp (sz, oz) != c)
     {
-      printf (" sz="); mpz_out_str (stdout, 10, sz); printf ("\n");
-      printf (" oz="); mpz_out_str (stdout, 10, oz); printf ("\n");
-      printf (" si=%ld\n", si);
-      abort ();
+      printf ("mpz_cmp_si (sz, oz) != %i.\n", -c);
+      goto fail;
     }
 
-  return 0;
+  if (mpz_fits_slong_p (sz))
+    {
+      if (!check_si (sz, si))
+	goto fail;
+      if (mpz_cmp_si (oz, si) != -c)
+	{
+	  printf ("mpz_cmp_si (oz, %ld) != %i.\n", si, -c);
+	  goto fail;
+	}
+    }
+  if (mpz_fits_slong_p (oz)) 
+    {
+      if (!check_si (oz, oi))
+	goto fail;
+      if (mpz_cmp_si (sz, oi) != c)
+	{
+	  printf ("mpz_cmp_si (sz, %ld) != %i.\n", oi, c);
+	  goto fail;
+	}
+    }
+  return;
+
+ fail:
+  printf (" sz="); mpz_out_str (stdout, 10, sz); printf ("\n");
+  printf (" si=%ld\n", si);
+  printf (" oz="); mpz_out_str (stdout, 10, oz); printf ("\n");
+  printf (" oi=%ld\n", si);
+  abort ();
 }
 
 void
@@ -73,6 +101,7 @@
 {
   long  si, oi;
   mpz_t sz, oz;
+  unsigned overflow_count;
 
   si = c;
   mpz_init_set_si (sz, si);
@@ -80,63 +109,84 @@
   oi = si;
   mpz_init_set (oz, sz);
 
-  do {
-    si *= 2; /* c * 2^k */
-    mpz_mul_2exp (sz, sz, 1);
-
-    if (check_si (sz, oz, si, oi, c))
-      {
-	mpz_set (oz, sz);
-	break;
-      }
-
-    oi = si + c; /* c * (2^k + 1) */
-    if (c == -1)
-      mpz_sub_ui (oz, sz, 1);
-    else
-      mpz_add_ui (oz, sz, 1);
+  /* To get a few tests with operands straddling the border, don't
+     stop at the very first operand exceeding a signed long. */
+  for (overflow_count = 0; overflow_count < 10; )
+    {
+      /* c * 2^k */
+      mpz_mul_2exp (sz, sz, 1);
+      if (mpz_fits_slong_p (sz))
+	si *= 2;
+      else
+	overflow_count++;
 
-    if (check_si (oz, sz, oi, si, c))
-      break;
+      check_si_cmp (sz, oz, si, oi, c);
+    
+      /* c * (2^k + 1) */
+      if (c == -1)
+	mpz_sub_ui (oz, sz, 1);
+      else
+	mpz_add_ui (oz, sz, 1);
+      if (mpz_fits_slong_p (oz))
+	oi = si + c;      
+      else 
+	overflow_count++;
+      check_si_cmp (oz, sz, oi, si, c);
 
-    oi = (si - c) * 2 + c; /* c * (2^K - 1) */
-    mpz_mul_si (oz, sz, 2*c);
-    if (c == -1)
-      mpz_ui_sub (oz, 1, oz); /* oz = sz * 2 + 1 */
-    else
-      mpz_sub_ui (oz, oz, 1); /* oz = sz * 2 - 1 */
-  } while (check_si (oz, sz, oi, si, c) == 0);
+      /* c * (2^K - 1) */
+      mpz_mul_si (oz, sz, 2*c);
+      if (c == -1)
+	mpz_ui_sub (oz, 1, oz); /* oz = sz * 2 + 1 */
+      else
+	mpz_sub_ui (oz, oz, 1); /* oz = sz * 2 - 1 */
+      if (mpz_fits_slong_p (oz))
+	oi = (si - c) * 2 + c; 
+      else 
+	overflow_count++;
+      
+      check_si_cmp (oz, sz, oi, si, c);
+    };
+  
+  mpz_clear (sz);
+  mpz_clear (oz);
+}
 
-  mpz_clear (sz);
-
-  if (mpz_fits_slong_p (oz))
+void 
+try_fits_slong_p (void)
+{
+  mpz_t x;
+  mpz_init_set_si (x, LONG_MAX);
+  if (!mpz_fits_slong_p (x))
     {
-      printf ("Should not fit a signed long any more.\n");
-      printf (" oz="); mpz_out_str (stdout, 10, oz); printf ("\n");
+      printf ("mpz_fits_slong_p (LONG_MAX) false!\n");
+      abort ();
+    }
+  mpz_add_ui (x, x, 1);
+  if (mpz_fits_slong_p (x))
+    {
+      printf ("mpz_fits_slong_p (LONG_MAX + 1) true!\n");
+      abort ();
+    }
+  mpz_set_si (x, LONG_MIN);
+  if (!mpz_fits_slong_p (x))
+    {
+      printf ("mpz_fits_slong_p (LONG_MIN) false!\n");
+      abort ();
+    }
+  mpz_sub_ui (x, x, 1);
+  if (mpz_fits_slong_p (x))
+    {
+      printf ("mpz_fits_slong_p (LONG_MIN - 1) true!\n");
       abort ();
     }
 
-  if (mpz_cmp_si (oz, -c) != c)
-      {
-	printf ("mpz_cmp_si (oz, %i) != %i.\n", c, c);
-	printf (" oz="); mpz_out_str (stdout, 10, oz); printf ("\n");
-	abort ();
-      }
-
-  mpz_mul_2exp (oz, oz, 1);
-  if (mpz_cmp_si (oz, -c) != c)
-      {
-	printf ("mpz_cmp_si (oz, %i) != %i.\n", c, c);
-	printf (" oz="); mpz_out_str (stdout, 10, oz); printf ("\n");
-	abort ();
-      }
-
-  mpz_clear (oz);
+  mpz_clear (x);
 }
 
 void
 testmain (int argc, char *argv[])
 {
+  try_fits_slong_p ();
   try_op_si (-1);
   try_op_si (1);
 }


More information about the gmp-commit mailing list