Skip to content

Commit 3850d4d

Browse files
committed
Avoid integer overflow hazard in interval_time().
When casting an interval to a time, the original code suffered from 64-bit integer overflow for inputs with a sufficiently large negative "time" field, leading to bogus results. Fix by rewriting the algorithm in a simpler form, that more obviously cannot overflow. While at it, improve the test coverage to include negative interval inputs. Discussion: https://postgr.es/m/CAEZATCXoUKHkcuq4q63hkiPsKZJd0kZWzgKtU%2BNT0aU4wbf_Pw%40mail.gmail.com
1 parent a4f7d33 commit 3850d4d

File tree

3 files changed

+17
-12
lines changed

3 files changed

+17
-12
lines changed

src/backend/utils/adt/date.c

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,19 +2012,10 @@ interval_time(PG_FUNCTION_ARGS)
20122012
{
20132013
Interval *span = PG_GETARG_INTERVAL_P(0);
20142014
TimeADT result;
2015-
int64 days;
20162015

2017-
result = span->time;
2018-
if (result >= USECS_PER_DAY)
2019-
{
2020-
days = result / USECS_PER_DAY;
2021-
result -= days * USECS_PER_DAY;
2022-
}
2023-
else if (result < 0)
2024-
{
2025-
days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
2026-
result += days * USECS_PER_DAY;
2027-
}
2016+
result = span->time % USECS_PER_DAY;
2017+
if (result < 0)
2018+
result += USECS_PER_DAY;
20282019

20292020
PG_RETURN_TIMEADT(result);
20302021
}

src/test/regress/expected/horology.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,18 @@ SELECT CAST(interval '02:03' AS time) AS "02:03:00";
981981
02:03:00
982982
(1 row)
983983

984+
SELECT CAST(interval '-02:03' AS time) AS "21:57:00";
985+
21:57:00
986+
----------
987+
21:57:00
988+
(1 row)
989+
990+
SELECT CAST(interval '-9223372022400000000 us' AS time) AS "00:00:00";
991+
00:00:00
992+
----------
993+
00:00:00
994+
(1 row)
995+
984996
SELECT time '01:30' + interval '02:01' AS "03:31:00";
985997
03:31:00
986998
----------

src/test/regress/sql/horology.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ SELECT d1 - interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL;
182182

183183
SELECT CAST(time '01:02' AS interval) AS "+01:02";
184184
SELECT CAST(interval '02:03' AS time) AS "02:03:00";
185+
SELECT CAST(interval '-02:03' AS time) AS "21:57:00";
186+
SELECT CAST(interval '-9223372022400000000 us' AS time) AS "00:00:00";
185187
SELECT time '01:30' + interval '02:01' AS "03:31:00";
186188
SELECT time '01:30' - interval '02:01' AS "23:29:00";
187189
SELECT time '02:30' + interval '36:01' AS "14:31:00";

0 commit comments

Comments
 (0)