From 34e2788ee8bdc075c997b5698fe55d0b1bdcfd86 Mon Sep 17 00:00:00 2001
From: Arne Goedeke <el@laramies.com>
Date: Fri, 11 Jan 2013 15:56:59 +0100
Subject: [PATCH] `%: add overflow checks

---
 src/bignum.h     | 10 ++++++++++
 src/operators.c  | 46 +++++++++++++++++++++++++++++++++++-----------
 src/testsuite.in |  2 ++
 3 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/src/bignum.h b/src/bignum.h
index 4e4f326216..cfbb024625 100644
--- a/src/bignum.h
+++ b/src/bignum.h
@@ -167,6 +167,16 @@ static INLINE int type ## _SUB_OVERFLOW(type a, type b) {			    \
     int of = 0;									    \
     DO_ ## type ## _SUB_OVERFLOW(a, b, &of);					    \
     return of;									    \
+}										    \
+static INLINE int type ## _MOD_OVERFLOW(type a, type b) {			    \
+    return type ## _DIV_OVERFLOW(a, b);						    \
+}										    \
+static INLINE type DO_ ## type ## _MOD_OVERFLOW(type a, type b, int * of) {	    \
+    if (type ## _MOD_OVERFLOW(a, b)) {						    \
+	*of = 1;								    \
+	return 0;								    \
+    }										    \
+    return a % b;								    \
 }
 
 
diff --git a/src/operators.c b/src/operators.c
index 7f94cb370e..32f1ca487b 100644
--- a/src/operators.c
+++ b/src/operators.c
@@ -4231,6 +4231,9 @@ PMOD_EXPORT void o_mod(void)
 {
   if(TYPEOF(sp[-2]) != TYPEOF(sp[-1]) && !float_promote())
   {
+#ifdef AUTO_BIGNUM
+do_lfun_modulo:
+#endif
     if(call_lfun(LFUN_MOD, LFUN_RMOD))
       return;
 
@@ -4305,28 +4308,49 @@ PMOD_EXPORT void o_mod(void)
     return;
   }
   case T_INT:
-    if (sp[-1].u.integer == 0)
+  {
+    int of = 0;
+    INT_TYPE a = sp[-2].u.integer,
+	     b = sp[-1].u.integer;
+    INT_TYPE res;
+    if (b == 0)
       OP_MODULO_BY_ZERO_ERROR("`%");
-    sp--;
-    if(sp[-1].u.integer>=0)
+    if(a>=0)
     {
-      if(sp[0].u.integer>=0)
+      if(b>=0)
       {
-	sp[-1].u.integer %= sp[0].u.integer;
+	res = a % b;
       }else{
-	sp[-1].u.integer=((sp[-1].u.integer+~sp[0].u.integer)%-sp[0].u.integer)-~sp[0].u.integer;
+	/* res = ((a+~b)%-b)-~b */
+	res = DO_INT_TYPE_ADD_OVERFLOW(a, ~b, &of);
+	res = DO_INT_TYPE_MOD_OVERFLOW(res, b, &of);
+	res = DO_INT_TYPE_SUB_OVERFLOW(res, ~b, &of);
       }
     }else{
-      if(sp[0].u.integer>=0)
+      if(b>=0)
       {
-	sp[-1].u.integer=sp[0].u.integer+~((~sp[-1].u.integer) % sp[0].u.integer);
+	/* res = b+~((~a) % b) */
+	res = DO_INT_TYPE_MOD_OVERFLOW(~a, b, &of);
+	res = DO_INT_TYPE_ADD_OVERFLOW(b, ~res, &of);
       }else{
-	sp[-1].u.integer=-(-sp[-1].u.integer % -sp[0].u.integer);
+	/* a % b and a % -b are equivalent, if overflow does not
+	 * happen
+	 * res = -(-a % -b) = a % b; */
+	res = DO_INT_TYPE_MOD_OVERFLOW(a, b, &of);
       }
     }
-    SET_SVAL_SUBTYPE(sp[-1], NUMBER_NUMBER);
+#ifdef AUTO_BIGNUM
+    if (of) {
+      stack_swap();
+      convert_stack_top_to_bignum();
+      stack_swap();
+      goto do_lfun_modulo;
+    }
+#endif
+    sp--;
+    SET_SVAL(sp[-1], T_INT, NUMBER_NUMBER, integer, res);
     return;
-
+  }
   default:
     PIKE_ERROR("`%", "Bad argument 1.\n", sp, 2);
   }
diff --git a/src/testsuite.in b/src/testsuite.in
index f03d6f84fa..e6a656d430 100644
--- a/src/testsuite.in
+++ b/src/testsuite.in
@@ -8374,6 +8374,8 @@ test_eq(0 ? "a" : 0 ? "b" : 1 ? "c" : 1, "c")
 
 // testing overflow checks
 test_eq(-1 - 0x7fffffff, -0x80000000)
+test_eq(Int.NATIVE_MIN % -1, 0)
+test_eq(Int.NATIVE_MAX % Int.NATIVE_MIN, -1)
 
 // testing indexing
 test_eq("foo"[0],'f')
-- 
GitLab