Commit 246693e5 authored by Tom Lane's avatar Tom Lane

Fix possible internal overflow in numeric multiplication.

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 d72f6c75,
so back-patch to all supported branches.
parent 7f11724b
...@@ -5789,9 +5789,15 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result, ...@@ -5789,9 +5789,15 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
* to avoid normalizing carries immediately. * to avoid normalizing carries immediately.
* *
* maxdig tracks the maximum possible value of any dig[] entry; when this * 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 * threatens to exceed INT_MAX, we take the time to propagate carries.
* avoid overflow in maxdig itself, it actually represents the max * Furthermore, we need to ensure that overflow doesn't occur during the
* possible value divided by NBASE-1. * 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)); dig = (int *) palloc0(res_ndigits * sizeof(int));
maxdig = 0; maxdig = 0;
...@@ -5806,7 +5812,7 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result, ...@@ -5806,7 +5812,7 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
/* Time to normalize? */ /* Time to normalize? */
maxdig += var1digit; maxdig += var1digit;
if (maxdig > INT_MAX / (NBASE - 1)) if (maxdig > (INT_MAX - INT_MAX / NBASE) / (NBASE - 1))
{ {
/* Yes, do it */ /* Yes, do it */
carry = 0; carry = 0;
......
...@@ -1333,6 +1333,33 @@ SELECT * FROM num_input_test; ...@@ -1333,6 +1333,33 @@ SELECT * FROM num_input_test;
NaN NaN
(7 rows) (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 -- Test some corner cases for division
-- --
......
...@@ -821,6 +821,18 @@ INSERT INTO num_input_test(n1) VALUES (' N aN '); ...@@ -821,6 +821,18 @@ INSERT INTO num_input_test(n1) VALUES (' N aN ');
SELECT * FROM num_input_test; 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 -- Test some corner cases for division
-- --
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment