Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4019285

Browse files
committed
Detect more overflows in timestamp[tz]_pl_interval.
In commit 25cd2d6 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
1 parent 310cd8a commit 4019285

File tree

3 files changed

+22
-4
lines changed

3 files changed

+22
-4
lines changed

src/backend/utils/adt/timestamp.c

+16-4
Original file line numberDiff line numberDiff line change
@@ -3091,7 +3091,10 @@ timestamp_pl_interval(PG_FUNCTION_ARGS)
30913091
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
30923092
errmsg("timestamp out of range")));
30933093

3094-
tm->tm_mon += span->month;
3094+
if (pg_add_s32_overflow(tm->tm_mon, span->month, &tm->tm_mon))
3095+
ereport(ERROR,
3096+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3097+
errmsg("timestamp out of range")));
30953098
if (tm->tm_mon > MONTHS_PER_YEAR)
30963099
{
30973100
tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
@@ -3143,7 +3146,10 @@ timestamp_pl_interval(PG_FUNCTION_ARGS)
31433146
errmsg("timestamp out of range")));
31443147
}
31453148

3146-
timestamp += span->time;
3149+
if (pg_add_s64_overflow(timestamp, span->time, &timestamp))
3150+
ereport(ERROR,
3151+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3152+
errmsg("timestamp out of range")));
31473153

31483154
if (!IS_VALID_TIMESTAMP(timestamp))
31493155
ereport(ERROR,
@@ -3233,7 +3239,10 @@ timestamptz_pl_interval_internal(TimestampTz timestamp,
32333239
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
32343240
errmsg("timestamp out of range")));
32353241

3236-
tm->tm_mon += span->month;
3242+
if (pg_add_s32_overflow(tm->tm_mon, span->month, &tm->tm_mon))
3243+
ereport(ERROR,
3244+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3245+
errmsg("timestamp out of range")));
32373246
if (tm->tm_mon > MONTHS_PER_YEAR)
32383247
{
32393248
tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
@@ -3292,7 +3301,10 @@ timestamptz_pl_interval_internal(TimestampTz timestamp,
32923301
errmsg("timestamp out of range")));
32933302
}
32943303

3295-
timestamp += span->time;
3304+
if (pg_add_s64_overflow(timestamp, span->time, &timestamp))
3305+
ereport(ERROR,
3306+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3307+
errmsg("timestamp out of range")));
32963308

32973309
if (!IS_VALID_TIMESTAMP(timestamp))
32983310
ereport(ERROR,

src/test/regress/expected/horology.out

+4
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@ SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '109203489 days'
484484

485485
SELECT timestamp without time zone '2000-01-01' - interval '2483590 days' AS "out of range";
486486
ERROR: timestamp out of range
487+
SELECT timestamp without time zone '294276-12-31 23:59:59' + interval '9223372036854775807 microseconds' AS "out of range";
488+
ERROR: timestamp out of range
487489
SELECT timestamp without time zone '12/31/294276' - timestamp without time zone '12/23/1999' AS "106751991 Days";
488490
106751991 Days
489491
------------------
@@ -746,6 +748,8 @@ SELECT timestamp with time zone '1999-12-01' + interval '1 month - 1 second' AS
746748

747749
SELECT timestamp with time zone '2000-01-01' - interval '2483590 days' AS "out of range";
748750
ERROR: timestamp out of range
751+
SELECT timestamp with time zone '294276-12-31 23:59:59 UTC' + interval '9223372036854775807 microseconds' AS "out of range";
752+
ERROR: timestamp out of range
749753
SELECT (timestamp with time zone 'today' = (timestamp with time zone 'yesterday' + interval '1 day')) as "True";
750754
True
751755
------

src/test/regress/sql/horology.sql

+2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '106000000 days'
121121
SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '107000000 days' AS "Jan 20, 288244";
122122
SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '109203489 days' AS "Dec 31, 294276";
123123
SELECT timestamp without time zone '2000-01-01' - interval '2483590 days' AS "out of range";
124+
SELECT timestamp without time zone '294276-12-31 23:59:59' + interval '9223372036854775807 microseconds' AS "out of range";
124125
SELECT timestamp without time zone '12/31/294276' - timestamp without time zone '12/23/1999' AS "106751991 Days";
125126

126127
-- Shorthand values
@@ -153,6 +154,7 @@ SELECT timestamp with time zone '1999-03-01' - interval '1 second' AS "Feb 28";
153154
SELECT timestamp with time zone '2000-03-01' - interval '1 second' AS "Feb 29";
154155
SELECT timestamp with time zone '1999-12-01' + interval '1 month - 1 second' AS "Dec 31";
155156
SELECT timestamp with time zone '2000-01-01' - interval '2483590 days' AS "out of range";
157+
SELECT timestamp with time zone '294276-12-31 23:59:59 UTC' + interval '9223372036854775807 microseconds' AS "out of range";
156158

157159
SELECT (timestamp with time zone 'today' = (timestamp with time zone 'yesterday' + interval '1 day')) as "True";
158160
SELECT (timestamp with time zone 'today' = (timestamp with time zone 'tomorrow' - interval '1 day')) as "True";

0 commit comments

Comments
 (0)