Detect more overflows in timestamp[tz]_pl_interval.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Apr 2024 17:42:13 +0000 (13:42 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Apr 2024 17:42:13 +0000 (13:42 -0400)
In commit 25cd2d640 I (tgl) opined that "The additions of the months
and microseconds fields could also overflow, of course.  However,
I believe we need no additional checks there; the existing range
checks should catch such cases".  This is demonstrably wrong however
for the microseconds field, and given that discovery it seems prudent
to be paranoid about the months addition as well.

Report and patch by Joseph Koshakow.  As before, back-patch to all
supported branches.  (However, the test case doesn't work before
v15 because we didn't allow wider-than-int32 numbers in interval
literals.  A variant test could probably be built that fits within
that restriction, but it didn't seem worth the trouble.)

Discussion: https://postgr.es/m/CAAvxfHf77sRHKoEzUw9_cMYSpbpNS2C+J_+8Dq4+0oi8iKopeA@mail.gmail.com

src/backend/utils/adt/timestamp.c

index 78d748cddaf43a5175296bdb8dd4537b46eb74b6..56bdb6b8d84ded844f7375233cee9fd37172f671 100644 (file)
@@ -2859,7 +2859,10 @@ timestamp_pl_interval(PG_FUNCTION_ARGS)
                        (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                         errmsg("timestamp out of range")));
 
-           tm->tm_mon += span->month;
+           if (pg_add_s32_overflow(tm->tm_mon, span->month, &tm->tm_mon))
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                        errmsg("timestamp out of range")));
            if (tm->tm_mon > MONTHS_PER_YEAR)
            {
                tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
@@ -2911,7 +2914,10 @@ timestamp_pl_interval(PG_FUNCTION_ARGS)
                         errmsg("timestamp out of range")));
        }
 
-       timestamp += span->time;
+       if (pg_add_s64_overflow(timestamp, span->time, &timestamp))
+           ereport(ERROR,
+                   (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                    errmsg("timestamp out of range")));
 
        if (!IS_VALID_TIMESTAMP(timestamp))
            ereport(ERROR,
@@ -2973,7 +2979,10 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
                        (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                         errmsg("timestamp out of range")));
 
-           tm->tm_mon += span->month;
+           if (pg_add_s32_overflow(tm->tm_mon, span->month, &tm->tm_mon))
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                        errmsg("timestamp out of range")));
            if (tm->tm_mon > MONTHS_PER_YEAR)
            {
                tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
@@ -3032,7 +3041,10 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
                         errmsg("timestamp out of range")));
        }
 
-       timestamp += span->time;
+       if (pg_add_s64_overflow(timestamp, span->time, &timestamp))
+           ereport(ERROR,
+                   (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                    errmsg("timestamp out of range")));
 
        if (!IS_VALID_TIMESTAMP(timestamp))
            ereport(ERROR,