]> granicus.if.org Git - postgresql/commitdiff
Fix possible internal overflow in numeric multiplication.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 21 Sep 2015 16:11:32 +0000 (12:11 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 21 Sep 2015 16:12:10 +0000 (12:12 -0400)
mul_var() postpones propagating carries until it risks overflow in its
internal digit array.  However, the logic failed to account for the
possibility of overflow in the carry propagation step, allowing wrong
results to be generated in corner cases.  We must slightly reduce the
when-to-propagate-carries threshold to avoid that.

Discovered and fixed by Dean Rasheed, with small adjustments by me.

This has been wrong since commit d72f6c75038d8d37e64a29a04b911f728044d83b,
so back-patch to all supported branches.

src/backend/utils/adt/numeric.c
src/test/regress/expected/numeric.out
src/test/regress/sql/numeric.sql

index 12a54bd1dbf0bb9a7faf13af9a1ae0e368489ab4..63a8da3ef79a8523275ab8f4f79c3628fb0fe89d 100644 (file)
@@ -4401,9 +4401,15 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
         * to avoid normalizing carries immediately.
         *
         * maxdig tracks the maximum possible value of any dig[] entry; when this
-        * threatens to exceed INT_MAX, we take the time to propagate carries. To
-        * avoid overflow in maxdig itself, it actually represents the max
-        * possible value divided by NBASE-1.
+        * threatens to exceed INT_MAX, we take the time to propagate carries.
+        * Furthermore, we need to ensure that overflow doesn't occur during the
+        * carry propagation passes either.  The carry values could be as much as
+        * INT_MAX/NBASE, so really we must normalize when digits threaten to
+        * exceed INT_MAX - INT_MAX/NBASE.
+        *
+        * To avoid overflow in maxdig itself, it actually represents the max
+        * possible value divided by NBASE-1, ie, at the top of the loop it is
+        * known that no dig[] entry exceeds maxdig * (NBASE-1).
         */
        dig = (int *) palloc0(res_ndigits * sizeof(int));
        maxdig = 0;
@@ -4418,7 +4424,7 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
 
                /* Time to normalize? */
                maxdig += var1digit;
-               if (maxdig > INT_MAX / (NBASE - 1))
+               if (maxdig > (INT_MAX - INT_MAX / NBASE) / (NBASE - 1))
                {
                        /* Yes, do it */
                        carry = 0;
index a8da4cdaa2d69e7a60bdd2af14b7f953ae49e520..65d2219e757e6dbaa2b415fcda6b6d19915ad23d 100644 (file)
@@ -1309,6 +1309,33 @@ SELECT * FROM num_input_test;
      NaN
 (7 rows)
 
+--
+-- Test some corner cases for multiplication
+--
+select 4790999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+                                                                                     ?column?                                                                                     
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 47909999999999999999999999999999999999999999999999999999999999999999999999999999999999985209000000000000000000000000000000000000000000000000000000000000000000000000000000000001
+(1 row)
+
+select 4789999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+                                                                                     ?column?                                                                                     
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 47899999999999999999999999999999999999999999999999999999999999999999999999999999999999985210000000000000000000000000000000000000000000000000000000000000000000000000000000000001
+(1 row)
+
+select 4770999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+                                                                                     ?column?                                                                                     
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 47709999999999999999999999999999999999999999999999999999999999999999999999999999999999985229000000000000000000000000000000000000000000000000000000000000000000000000000000000001
+(1 row)
+
+select 4769999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+                                                                                     ?column?                                                                                     
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 47699999999999999999999999999999999999999999999999999999999999999999999999999999999999985230000000000000000000000000000000000000000000000000000000000000000000000000000000000001
+(1 row)
+
 --
 -- Test some corner cases for division
 --
index 5c08717e7a968380af71ab4ecfaa7af6e2641477..f7e1bc9862e1a196b6b2aeb4c9faedfe1cf4da1b 100644 (file)
@@ -811,6 +811,18 @@ INSERT INTO num_input_test(n1) VALUES (' N aN ');
 
 SELECT * FROM num_input_test;
 
+--
+-- Test some corner cases for multiplication
+--
+
+select 4790999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+
+select 4789999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+
+select 4770999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+
+select 4769999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+
 --
 -- Test some corner cases for division
 --