postgres.git / summary / log / commit / refs

commit    728a2ac214d8084cdc471fe18ece8d2699f47814
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Tue Nov 17 20:46:47 2015 +0000
First released in: 9.6.0, 9.1.20, 9.2.15, 9.3.11, 9.4.6, 9.5.0

    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


src/backend/utils/adt/numeric.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 63a8da3ef79..ba1f6c272fc 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -4872,8 +4872,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; @@ -4899,7 +4905,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; [parent: 7b21d1bcafef]