Avoid integer overflow hazard in interval_time().
authorDean Rasheed <dean.a.rasheed@gmail.com>
Thu, 9 Nov 2023 12:10:14 +0000 (12:10 +0000)
committerDean Rasheed <dean.a.rasheed@gmail.com>
Thu, 9 Nov 2023 12:10:14 +0000 (12:10 +0000)
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

src/backend/utils/adt/date.c
src/test/regress/expected/horology.out
src/test/regress/sql/horology.sql

index 56c7746c11fcf283ef1e06dd9b4894be1592a148..544e1d32bfc084c8abc6617843eb34ae5bea40ba 100644 (file)
@@ -2012,19 +2012,10 @@ interval_time(PG_FUNCTION_ARGS)
 {
        Interval   *span = PG_GETARG_INTERVAL_P(0);
        TimeADT         result;
-       int64           days;
 
-       result = span->time;
-       if (result >= USECS_PER_DAY)
-       {
-               days = result / USECS_PER_DAY;
-               result -= days * USECS_PER_DAY;
-       }
-       else if (result < 0)
-       {
-               days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
-               result += days * USECS_PER_DAY;
-       }
+       result = span->time % USECS_PER_DAY;
+       if (result < 0)
+               result += USECS_PER_DAY;
 
        PG_RETURN_TIMEADT(result);
 }
index e63e5b30fee8e16f979bc5372be6c97e1d41bf35..8f52661096f839cf24719984dfababf58be35681 100644 (file)
@@ -981,6 +981,18 @@ SELECT CAST(interval '02:03' AS time) AS "02:03:00";
  02:03:00
 (1 row)
 
+SELECT CAST(interval '-02:03' AS time) AS "21:57:00";
+ 21:57:00 
+----------
+ 21:57:00
+(1 row)
+
+SELECT CAST(interval '-9223372022400000000 us' AS time) AS "00:00:00";
+ 00:00:00 
+----------
+ 00:00:00
+(1 row)
+
 SELECT time '01:30' + interval '02:01' AS "03:31:00";
  03:31:00 
 ----------
index f7f8c8d2dd9eba2b116a97239a7b3274a5f33353..39a35a6b7ce1bdd801ff7ea2f70dc2f15d288756 100644 (file)
@@ -182,6 +182,8 @@ SELECT d1 - interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL;
 
 SELECT CAST(time '01:02' AS interval) AS "+01:02";
 SELECT CAST(interval '02:03' AS time) AS "02:03:00";
+SELECT CAST(interval '-02:03' AS time) AS "21:57:00";
+SELECT CAST(interval '-9223372022400000000 us' AS time) AS "00:00:00";
 SELECT time '01:30' + interval '02:01' AS "03:31:00";
 SELECT time '01:30' - interval '02:01' AS "23:29:00";
 SELECT time '02:30' + interval '36:01' AS "14:31:00";