From 93ffbeb2b10cec642e855b960d76aba62f8a2496 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Sat, 1 Apr 2023 10:22:53 -0400 Subject: [PATCH v20 2/6] Check for overflow in make_interval --- src/backend/utils/adt/timestamp.c | 30 ++++++++++++++++++++------ src/include/common/int.h | 13 +++++++++++ src/include/datatype/timestamp.h | 1 + src/test/regress/expected/interval.out | 7 ++++++ src/test/regress/sql/interval.sql | 5 +++++ 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 0e50aaec..3ab9b691 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -1518,13 +1518,31 @@ make_interval(PG_FUNCTION_ARGS) errmsg("interval out of range"))); result = (Interval *) palloc(sizeof(Interval)); - result->month = years * MONTHS_PER_YEAR + months; - result->day = weeks * 7 + days; + result->month = months; + if (pg_mul_add_s32_overflow(years, MONTHS_PER_YEAR, &result->month)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + result->day = days; + if (pg_mul_add_s32_overflow(weeks, DAYS_PER_WEEK, &result->day)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); - secs = rint(secs * USECS_PER_SEC); - result->time = hours * ((int64) SECS_PER_HOUR * USECS_PER_SEC) + - mins * ((int64) SECS_PER_MINUTE * USECS_PER_SEC) + - (int64) secs; + secs = rint(float8_mul(secs, USECS_PER_SEC)); + if (!FLOAT8_FITS_IN_INT64(secs)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + result->time = (int64) secs; + if (pg_mul_add_s64_overflow(mins, ((int64) SECS_PER_MINUTE * USECS_PER_SEC), &result->time)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + if (pg_mul_add_s64_overflow(hours, ((int64) SECS_PER_HOUR * USECS_PER_SEC), &result->time)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); PG_RETURN_INTERVAL_P(result); } diff --git a/src/include/common/int.h b/src/include/common/int.h index 81726c65..48ef4955 100644 --- a/src/include/common/int.h +++ b/src/include/common/int.h @@ -154,6 +154,19 @@ pg_mul_s32_overflow(int32 a, int32 b, int32 *result) #endif } +/* + * Add val * multiplier to *sum. + * Returns false if successful, true on overflow. + */ +static inline bool +pg_mul_add_s32_overflow(int32 val, int32 multiplier, int32 *sum) +{ + int32 product; + + return pg_mul_s32_overflow(val, multiplier, &product) || + pg_add_s32_overflow(*sum, product, sum); +} + /* * INT64 */ diff --git a/src/include/datatype/timestamp.h b/src/include/datatype/timestamp.h index ab8ccf89..1a639058 100644 --- a/src/include/datatype/timestamp.h +++ b/src/include/datatype/timestamp.h @@ -114,6 +114,7 @@ struct pg_itm_in * 30 days. */ #define DAYS_PER_MONTH 30 /* assumes exactly 30 days per month */ +#define DAYS_PER_WEEK 7 #define HOURS_PER_DAY 24 /* assume no daylight savings time changes */ /* diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index c0ca8e04..96db07c3 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -1587,6 +1587,13 @@ select interval '-2147483648 months -2147483648 days -9223372036854775808 micros ERROR: interval field value out of range: "-2147483648 months -2147483648 days -9223372036854775808 microseconds ago" LINE 1: select interval '-2147483648 months -2147483648 days -922337... ^ +-- overflowing using make_interval +select make_interval(1, 2147483647, 2147483647, 1, 1, 1, 9223372036854.7759); +ERROR: interval out of range +select make_interval(-1, -2147483648, -2147483648, -1, -1, -1, -9223372036854.7759); +ERROR: interval out of range +SELECT make_interval(0, 0, 0, 0, 0, 0, 9223372036854.776000); +ERROR: interval out of range -- test that INT_MIN number is formatted properly SET IntervalStyle to postgres; select interval '-2147483648 months -2147483648 days -9223372036854775808 us'; diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 038fc508..f328b891 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -511,6 +511,11 @@ select interval '-2147483648 days ago'; select interval '-9223372036854775808 microseconds ago'; select interval '-2147483648 months -2147483648 days -9223372036854775808 microseconds ago'; +-- overflowing using make_interval +select make_interval(1, 2147483647, 2147483647, 1, 1, 1, 9223372036854.7759); +select make_interval(-1, -2147483648, -2147483648, -1, -1, -1, -9223372036854.7759); +SELECT make_interval(0, 0, 0, 0, 0, 0, 9223372036854.776000); + -- test that INT_MIN number is formatted properly SET IntervalStyle to postgres; select interval '-2147483648 months -2147483648 days -9223372036854775808 us'; -- 2.34.1