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

Commit b2d5544

Browse files
committed
Guard against overflow in make_interval().
The original code did very little to guard against integer or floating point overflow when computing the interval's fields. Detect any such overflows and error out, rather than silently returning bogus results. Joseph Koshakow, reviewed by Ashutosh Bapat and me. Discussion: https://postgr.es/m/CAAvxfHcm1TPwH_zaGWuFoL8pZBestbRZTU6Z%3D-RvAdSXTPbKfg%40mail.gmail.com
1 parent 849172f commit b2d5544

File tree

4 files changed

+71
-11
lines changed

4 files changed

+71
-11
lines changed

src/backend/utils/adt/timestamp.c

+28-11
Original file line numberDiff line numberDiff line change
@@ -1509,24 +1509,41 @@ make_interval(PG_FUNCTION_ARGS)
15091509
Interval *result;
15101510

15111511
/*
1512-
* Reject out-of-range inputs. We really ought to check the integer
1513-
* inputs as well, but it's not entirely clear what limits to apply.
1512+
* Reject out-of-range inputs. We reject any input values that cause
1513+
* integer overflow of the corresponding interval fields.
15141514
*/
15151515
if (isinf(secs) || isnan(secs))
1516-
ereport(ERROR,
1517-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1518-
errmsg("interval out of range")));
1516+
goto out_of_range;
15191517

15201518
result = (Interval *) palloc(sizeof(Interval));
1521-
result->month = years * MONTHS_PER_YEAR + months;
1522-
result->day = weeks * 7 + days;
15231519

1524-
secs = rint(secs * USECS_PER_SEC);
1525-
result->time = hours * ((int64) SECS_PER_HOUR * USECS_PER_SEC) +
1526-
mins * ((int64) SECS_PER_MINUTE * USECS_PER_SEC) +
1527-
(int64) secs;
1520+
/* years and months -> months */
1521+
if (pg_mul_s32_overflow(years, MONTHS_PER_YEAR, &result->month) ||
1522+
pg_add_s32_overflow(result->month, months, &result->month))
1523+
goto out_of_range;
1524+
1525+
/* weeks and days -> days */
1526+
if (pg_mul_s32_overflow(weeks, DAYS_PER_WEEK, &result->day) ||
1527+
pg_add_s32_overflow(result->day, days, &result->day))
1528+
goto out_of_range;
1529+
1530+
/* hours and mins -> usecs (cannot overflow 64-bit) */
1531+
result->time = hours * USECS_PER_HOUR + mins * USECS_PER_MINUTE;
1532+
1533+
/* secs -> usecs */
1534+
secs = rint(float8_mul(secs, USECS_PER_SEC));
1535+
if (!FLOAT8_FITS_IN_INT64(secs) ||
1536+
pg_add_s64_overflow(result->time, (int64) secs, &result->time))
1537+
goto out_of_range;
15281538

15291539
PG_RETURN_INTERVAL_P(result);
1540+
1541+
out_of_range:
1542+
ereport(ERROR,
1543+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1544+
errmsg("interval out of range"));
1545+
1546+
PG_RETURN_NULL(); /* keep compiler quiet */
15301547
}
15311548

15321549
/* EncodeSpecialTimestamp()

src/include/datatype/timestamp.h

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ struct pg_itm_in
114114
* 30 days.
115115
*/
116116
#define DAYS_PER_MONTH 30 /* assumes exactly 30 days per month */
117+
#define DAYS_PER_WEEK 7
117118
#define HOURS_PER_DAY 24 /* assume no daylight savings time changes */
118119

119120
/*

src/test/regress/expected/interval.out

+27
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,33 @@ select interval '-2147483648 months -2147483648 days -9223372036854775808 micros
15871587
ERROR: interval field value out of range: "-2147483648 months -2147483648 days -9223372036854775808 microseconds ago"
15881588
LINE 1: select interval '-2147483648 months -2147483648 days -922337...
15891589
^
1590+
-- overflowing using make_interval
1591+
select make_interval(years := 178956971);
1592+
ERROR: interval out of range
1593+
select make_interval(years := -178956971);
1594+
ERROR: interval out of range
1595+
select make_interval(years := 1, months := 2147483647);
1596+
ERROR: interval out of range
1597+
select make_interval(years := -1, months := -2147483648);
1598+
ERROR: interval out of range
1599+
select make_interval(weeks := 306783379);
1600+
ERROR: interval out of range
1601+
select make_interval(weeks := -306783379);
1602+
ERROR: interval out of range
1603+
select make_interval(weeks := 1, days := 2147483647);
1604+
ERROR: interval out of range
1605+
select make_interval(weeks := -1, days := -2147483648);
1606+
ERROR: interval out of range
1607+
select make_interval(secs := 1e308);
1608+
ERROR: value out of range: overflow
1609+
select make_interval(secs := 1e18);
1610+
ERROR: interval out of range
1611+
select make_interval(secs := -1e18);
1612+
ERROR: interval out of range
1613+
select make_interval(mins := 1, secs := 9223372036800.0);
1614+
ERROR: interval out of range
1615+
select make_interval(mins := -1, secs := -9223372036800.0);
1616+
ERROR: interval out of range
15901617
-- test that INT_MIN number is formatted properly
15911618
SET IntervalStyle to postgres;
15921619
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';

src/test/regress/sql/interval.sql

+15
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ select interval '-2147483648 days ago';
511511
select interval '-9223372036854775808 microseconds ago';
512512
select interval '-2147483648 months -2147483648 days -9223372036854775808 microseconds ago';
513513

514+
-- overflowing using make_interval
515+
select make_interval(years := 178956971);
516+
select make_interval(years := -178956971);
517+
select make_interval(years := 1, months := 2147483647);
518+
select make_interval(years := -1, months := -2147483648);
519+
select make_interval(weeks := 306783379);
520+
select make_interval(weeks := -306783379);
521+
select make_interval(weeks := 1, days := 2147483647);
522+
select make_interval(weeks := -1, days := -2147483648);
523+
select make_interval(secs := 1e308);
524+
select make_interval(secs := 1e18);
525+
select make_interval(secs := -1e18);
526+
select make_interval(mins := 1, secs := 9223372036800.0);
527+
select make_interval(mins := -1, secs := -9223372036800.0);
528+
514529
-- test that INT_MIN number is formatted properly
515530
SET IntervalStyle to postgres;
516531
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';

0 commit comments

Comments
 (0)