]> granicus.if.org Git - postgresql/commitdiff
Add checks for interval overflow/underflow
authorBruce Momjian <bruce@momjian.us>
Thu, 30 Jan 2014 14:41:43 +0000 (09:41 -0500)
committerBruce Momjian <bruce@momjian.us>
Thu, 30 Jan 2014 14:41:43 +0000 (09:41 -0500)
New checks include input, month/day/time internal adjustments, addition,
subtraction, multiplication, and negation.  Also adjust docs to
correctly specify interval size in bytes.

Report from Rok Kralj

doc/src/sgml/datatype.sgml
src/backend/utils/adt/datetime.c
src/backend/utils/adt/timestamp.c
src/interfaces/ecpg/pgtypeslib/interval.c

index 6bf4cf61d8659d48df9cd3cd41a42792f88f0810..b7d7d80028708c39e1dcc49e428fbeddae765e73 100644 (file)
@@ -1587,7 +1587,7 @@ SELECT E'\\xDEADBEEF';
        </row>
        <row>
         <entry><type>interval [ <replaceable>fields</replaceable> ] [ (<replaceable>p</replaceable>) ]</type></entry>
-        <entry>12 bytes</entry>
+        <entry>16 bytes</entry>
         <entry>time interval</entry>
         <entry>-178000000 years</entry>
         <entry>178000000 years</entry>
index a61b40e17f949c9f90cbf126351bd75910d39709..946adfad997976fd823b144f8ca4a4cc31571af9 100644 (file)
@@ -2976,6 +2976,9 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                                        type = DTK_MONTH;
                                        if (*field[i] == '-')
                                                val2 = -val2;
+                                       if (((double)val * MONTHS_PER_YEAR + val2) > INT_MAX ||
+                                               ((double)val * MONTHS_PER_YEAR + val2) < INT_MIN)
+                                               return DTERR_FIELD_OVERFLOW;
                                        val = val * MONTHS_PER_YEAR + val2;
                                        fval = 0;
                                }
index 4581862a3fee836d7e8e92281194b8b78483e23f..cf6982b95dd835e9da93b04107cdf08675efc8af 100644 (file)
 #error -ffast-math is known to break this code
 #endif
 
+#define SAMESIGN(a,b)   (((a) < 0) == ((b) < 0))
+
+#ifndef INT64_MAX
+#define INT64_MAX      INT64CONST(0x7FFFFFFFFFFFFFFF)
+#endif
+
+#ifndef INT64_MIN
+#define INT64_MIN      (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1)
+#endif
 
 /* Set at postmaster start */
 TimestampTz PgStartTime;
@@ -1694,7 +1703,11 @@ interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
 #ifdef HAVE_INT64_TIMESTAMP
        tfrac = time / USECS_PER_HOUR;
        time -= tfrac * USECS_PER_HOUR;
-       tm->tm_hour = tfrac;            /* could overflow ... */
+       tm->tm_hour = tfrac;
+       if (!SAMESIGN(tm->tm_hour, tfrac))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
        tfrac = time / USECS_PER_MINUTE;
        time -= tfrac * USECS_PER_MINUTE;
        tm->tm_min = tfrac;
@@ -1725,7 +1738,11 @@ recalc:
 int
 tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
 {
-       span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
+       double total_months = (double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
+
+       if (total_months > INT_MAX || total_months < INT_MIN)
+               return -1;
+       span->month = total_months;
        span->day = tm->tm_mday;
 #ifdef HAVE_INT64_TIMESTAMP
        span->time = (((((tm->tm_hour * INT64CONST(60)) +
@@ -2826,8 +2843,21 @@ interval_um(PG_FUNCTION_ARGS)
        result = (Interval *) palloc(sizeof(Interval));
 
        result->time = -interval->time;
+       /* overflow check copied from int4um */
+       if (interval->time != 0 && SAMESIGN(result->time, interval->time))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
        result->day = -interval->day;
+       if (interval->day != 0 && SAMESIGN(result->day, interval->day))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
        result->month = -interval->month;
+       if (interval->month != 0 && SAMESIGN(result->month, interval->month))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
 
        PG_RETURN_INTERVAL_P(result);
 }
@@ -2872,8 +2902,26 @@ interval_pl(PG_FUNCTION_ARGS)
        result = (Interval *) palloc(sizeof(Interval));
 
        result->month = span1->month + span2->month;
+       /* overflow check copied from int4pl */
+       if (SAMESIGN(span1->month, span2->month) &&
+               !SAMESIGN(result->month, span1->month))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+
        result->day = span1->day + span2->day;
+       if (SAMESIGN(span1->day, span2->day) &&
+               !SAMESIGN(result->day, span1->day))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+
        result->time = span1->time + span2->time;
+       if (SAMESIGN(span1->time, span2->time) &&
+               !SAMESIGN(result->time, span1->time))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
 
        PG_RETURN_INTERVAL_P(result);
 }
@@ -2888,8 +2936,27 @@ interval_mi(PG_FUNCTION_ARGS)
        result = (Interval *) palloc(sizeof(Interval));
 
        result->month = span1->month - span2->month;
+       /* overflow check copied from int4mi */
+       if (!SAMESIGN(span1->month, span2->month) &&
+               !SAMESIGN(result->month, span1->month))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+
        result->day = span1->day - span2->day;
+       if (!SAMESIGN(span1->day, span2->day) &&
+               !SAMESIGN(result->day, span1->day))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+
        result->time = span1->time - span2->time;
+       if (!SAMESIGN(span1->time, span2->time) &&
+               !SAMESIGN(result->time, span1->time))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+
 
        PG_RETURN_INTERVAL_P(result);
 }
@@ -2906,15 +2973,27 @@ interval_mul(PG_FUNCTION_ARGS)
        Interval   *span = PG_GETARG_INTERVAL_P(0);
        float8          factor = PG_GETARG_FLOAT8(1);
        double          month_remainder_days,
-                               sec_remainder;
+                               sec_remainder,
+                               result_double;
        int32           orig_month = span->month,
                                orig_day = span->day;
        Interval   *result;
 
        result = (Interval *) palloc(sizeof(Interval));
 
-       result->month = (int32) (span->month * factor);
-       result->day = (int32) (span->day * factor);
+       result_double = span->month * factor;
+       if (result_double > INT_MAX || result_double < INT_MIN)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+       result->month = (int32) result_double;
+
+       result_double = span->day * factor;
+       if (result_double > INT_MAX || result_double < INT_MIN)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+       result->day = (int32) result_double;
 
        /*
         * The above correctly handles the whole-number part of the month and day
@@ -2954,7 +3033,12 @@ interval_mul(PG_FUNCTION_ARGS)
        /* cascade units down */
        result->day += (int32) month_remainder_days;
 #ifdef HAVE_INT64_TIMESTAMP
-       result->time = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
+       result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
+       if (result_double > INT64_MAX || result_double < INT64_MIN)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("interval out of range")));
+       result->time = (int64) result_double;
 #else
        result->time = span->time * factor + sec_remainder;
 #endif
index efa775de15aafe6d75191580c4717db203766c8a..6d0926882e87b9ee471ee7c2042a267e615626e1 100644 (file)
@@ -1036,6 +1036,9 @@ recalc:
 static int
 tm2interval(struct tm * tm, fsec_t fsec, interval * span)
 {
+       if ((double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon > INT_MAX ||
+               (double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon < INT_MIN)
+               return -1;
        span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
 #ifdef HAVE_INT64_TIMESTAMP
        span->time = (((((((tm->tm_mday * INT64CONST(24)) +