Hi all, Attached is a patch that fixes some overflow/underflow hazards that I discovered in the interval rounding code.
The lines look a bit long, but I did run the following before committing: `$ curl https://buildfarm.postgresql.org/cgi-bin/typedefs.pl -o src/tools/pgindent/typedefs.list && src/tools/pgindent/pgindent src/backend/utils/adt/timestamp.c` Thanks, Joe Koshakow
From 389b0d1e3f3cca6fca1e45fdd202b1ca066326c2 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow <kosh...@gmail.com> Date: Tue, 13 Feb 2024 13:06:13 -0500 Subject: [PATCH] Fix overflow hazard in interval rounding This commit fixes overflow/underflow hazards present in the interval rounding code used to parse intervals. --- src/backend/utils/adt/timestamp.c | 18 ++++++++++-------- src/test/regress/expected/interval.out | 9 +++++++++ src/test/regress/sql/interval.sql | 5 +++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index c38f88dba7..a3b65a755f 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -1509,17 +1509,19 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod, if (interval->time >= INT64CONST(0)) { - interval->time = ((interval->time + - IntervalOffsets[precision]) / - IntervalScales[precision]) * - IntervalScales[precision]; + if (pg_add_s64_overflow(interval->time, IntervalOffsets[precision], &interval->time)) + ereport(ERROR, + errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range")); + interval->time = (interval->time / IntervalScales[precision]) * IntervalScales[precision]; } else { - interval->time = -(((-interval->time + - IntervalOffsets[precision]) / - IntervalScales[precision]) * - IntervalScales[precision]); + if (pg_sub_s64_overflow(IntervalOffsets[precision], interval->time, &interval->time)) + ereport(ERROR, + errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range")); + interval->time = -((interval->time / IntervalScales[precision]) * IntervalScales[precision]); } } } diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index b79b6fcd4d..055930ccac 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -929,6 +929,15 @@ SELECT interval '1 2:03:04.5678' minute to second(2); 1 day 02:03:04.57 (1 row) +-- these should fail as out-of-range +SELECT interval '2562047788:00:54.775807' SECOND(2); +ERROR: interval out of range +LINE 1: SELECT interval '2562047788:00:54.775807' SECOND(2); + ^ +SELECT interval '-2562047788:00:54.775807' SECOND(2); +ERROR: interval out of range +LINE 1: SELECT interval '-2562047788:00:54.775807' SECOND(2); + ^ -- test casting to restricted precision (bug #14479) SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes", (f1 + INTERVAL '1 month')::INTERVAL MONTH::INTERVAL YEAR AS "years" diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 5566ad0e51..838da2cc13 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -270,6 +270,11 @@ SELECT interval '1 2:03:04.5678' hour to second(2); SELECT interval '1 2.3456' minute to second(2); SELECT interval '1 2:03.5678' minute to second(2); SELECT interval '1 2:03:04.5678' minute to second(2); +-- these should fail as out-of-range +SELECT interval '2562047788:00:54.775807' SECOND(2); +SELECT interval '-2562047788:00:54.775807' SECOND(2); + + -- test casting to restricted precision (bug #14479) SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes", -- 2.34.1