aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2015-11-17 15:46:47 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2015-11-17 15:46:47 -0500
commit5f10b7a604c87fc61a2c20a56552301f74c9bd5f (patch)
tree3d73f5b266ff532117ffc9b15778f8fa16b1dab8 /src/backend/utils/adt
parentc5ec4064120c12c7b8cd4772d0b9f571f5dd40b4 (diff)
downloadpostgresql-5f10b7a604c87fc61a2c20a56552301f74c9bd5f.tar.gz
postgresql-5f10b7a604c87fc61a2c20a56552301f74c9bd5f.zip
Fix possible internal overflow in numeric division.
div_var_fast() postpones propagating carries in the same way as mul_var(), so it has the same corner-case overflow risk we fixed in 246693e5ae8a36f0, namely that the size of the carries has to be accounted for when setting the threshold for executing a carry propagation step. We've not devised a test case illustrating the brokenness, but the required fix seems clear enough. Like the previous fix, back-patch to all active branches. Dean Rasheed
Diffstat (limited to 'src/backend/utils/adt')
-rw-r--r--src/backend/utils/adt/numeric.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index fe9f3f7a506..dcdc5cf8300 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -6266,8 +6266,14 @@ div_var_fast(NumericVar *var1, NumericVar *var2, NumericVar *result,
/*
* maxdiv tracks the maximum possible absolute value of any div[] entry;
* when this threatens to exceed INT_MAX, we take the time to propagate
- * carries. To avoid overflow in maxdiv itself, it actually represents
- * the max possible abs. value divided by NBASE-1.
+ * carries. Furthermore, we need to ensure that overflow doesn't occur
+ * during the carry propagation passes either. The carry values may have
+ * an absolute value as high as INT_MAX/NBASE + 1, so really we must
+ * normalize when digits threaten to exceed INT_MAX - INT_MAX/NBASE - 1.
+ *
+ * To avoid overflow in maxdiv itself, it represents the max absolute
+ * value divided by NBASE-1, ie, at the top of the loop it is known that
+ * no div[] entry has an absolute value exceeding maxdiv * (NBASE-1).
*/
maxdiv = 1;
@@ -6293,7 +6299,7 @@ div_var_fast(NumericVar *var1, NumericVar *var2, NumericVar *result,
{
/* Do we need to normalize now? */
maxdiv += Abs(qdigit);
- if (maxdiv > INT_MAX / (NBASE - 1))
+ if (maxdiv > (INT_MAX - INT_MAX / NBASE - 1) / (NBASE - 1))
{
/* Yes, do it */
carry = 0;