Skip to content

Commit 146604e

Browse files
committed
Add checks for interval overflow/underflow
New checks include input, month/day/time internal adjustments, addition, subtraction, multiplication, and negation. Also adjust docs to correctly specify interval size in bytes. Report from Rok Kralj
1 parent 571addd commit 146604e

File tree

4 files changed

+97
-7
lines changed

4 files changed

+97
-7
lines changed

doc/src/sgml/datatype.sgml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1587,7 +1587,7 @@ SELECT E'\\xDEADBEEF';
15871587
</row>
15881588
<row>
15891589
<entry><type>interval [ <replaceable>fields</replaceable> ] [ (<replaceable>p</replaceable>) ]</type></entry>
1590-
<entry>12 bytes</entry>
1590+
<entry>16 bytes</entry>
15911591
<entry>time interval</entry>
15921592
<entry>-178000000 years</entry>
15931593
<entry>178000000 years</entry>

src/backend/utils/adt/datetime.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,6 +2976,9 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
29762976
type = DTK_MONTH;
29772977
if (*field[i] == '-')
29782978
val2 = -val2;
2979+
if (((double)val * MONTHS_PER_YEAR + val2) > INT_MAX ||
2980+
((double)val * MONTHS_PER_YEAR + val2) < INT_MIN)
2981+
return DTERR_FIELD_OVERFLOW;
29792982
val = val * MONTHS_PER_YEAR + val2;
29802983
fval = 0;
29812984
}

src/backend/utils/adt/timestamp.c

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@
4141
#error -ffast-math is known to break this code
4242
#endif
4343

44+
#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
45+
46+
#ifndef INT64_MAX
47+
#define INT64_MAX INT64CONST(0x7FFFFFFFFFFFFFFF)
48+
#endif
49+
50+
#ifndef INT64_MIN
51+
#define INT64_MIN (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1)
52+
#endif
4453

4554
/* Set at postmaster start */
4655
TimestampTz PgStartTime;
@@ -1694,7 +1703,11 @@ interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
16941703
#ifdef HAVE_INT64_TIMESTAMP
16951704
tfrac = time / USECS_PER_HOUR;
16961705
time -= tfrac * USECS_PER_HOUR;
1697-
tm->tm_hour = tfrac; /* could overflow ... */
1706+
tm->tm_hour = tfrac;
1707+
if (!SAMESIGN(tm->tm_hour, tfrac))
1708+
ereport(ERROR,
1709+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1710+
errmsg("interval out of range")));
16981711
tfrac = time / USECS_PER_MINUTE;
16991712
time -= tfrac * USECS_PER_MINUTE;
17001713
tm->tm_min = tfrac;
@@ -1725,7 +1738,11 @@ interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
17251738
int
17261739
tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
17271740
{
1728-
span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
1741+
double total_months = (double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
1742+
1743+
if (total_months > INT_MAX || total_months < INT_MIN)
1744+
return -1;
1745+
span->month = total_months;
17291746
span->day = tm->tm_mday;
17301747
#ifdef HAVE_INT64_TIMESTAMP
17311748
span->time = (((((tm->tm_hour * INT64CONST(60)) +
@@ -2826,8 +2843,21 @@ interval_um(PG_FUNCTION_ARGS)
28262843
result = (Interval *) palloc(sizeof(Interval));
28272844

28282845
result->time = -interval->time;
2846+
/* overflow check copied from int4um */
2847+
if (interval->time != 0 && SAMESIGN(result->time, interval->time))
2848+
ereport(ERROR,
2849+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2850+
errmsg("interval out of range")));
28292851
result->day = -interval->day;
2852+
if (interval->day != 0 && SAMESIGN(result->day, interval->day))
2853+
ereport(ERROR,
2854+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2855+
errmsg("interval out of range")));
28302856
result->month = -interval->month;
2857+
if (interval->month != 0 && SAMESIGN(result->month, interval->month))
2858+
ereport(ERROR,
2859+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2860+
errmsg("interval out of range")));
28312861

28322862
PG_RETURN_INTERVAL_P(result);
28332863
}
@@ -2872,8 +2902,26 @@ interval_pl(PG_FUNCTION_ARGS)
28722902
result = (Interval *) palloc(sizeof(Interval));
28732903

28742904
result->month = span1->month + span2->month;
2905+
/* overflow check copied from int4pl */
2906+
if (SAMESIGN(span1->month, span2->month) &&
2907+
!SAMESIGN(result->month, span1->month))
2908+
ereport(ERROR,
2909+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2910+
errmsg("interval out of range")));
2911+
28752912
result->day = span1->day + span2->day;
2913+
if (SAMESIGN(span1->day, span2->day) &&
2914+
!SAMESIGN(result->day, span1->day))
2915+
ereport(ERROR,
2916+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2917+
errmsg("interval out of range")));
2918+
28762919
result->time = span1->time + span2->time;
2920+
if (SAMESIGN(span1->time, span2->time) &&
2921+
!SAMESIGN(result->time, span1->time))
2922+
ereport(ERROR,
2923+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2924+
errmsg("interval out of range")));
28772925

28782926
PG_RETURN_INTERVAL_P(result);
28792927
}
@@ -2888,8 +2936,27 @@ interval_mi(PG_FUNCTION_ARGS)
28882936
result = (Interval *) palloc(sizeof(Interval));
28892937

28902938
result->month = span1->month - span2->month;
2939+
/* overflow check copied from int4mi */
2940+
if (!SAMESIGN(span1->month, span2->month) &&
2941+
!SAMESIGN(result->month, span1->month))
2942+
ereport(ERROR,
2943+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2944+
errmsg("interval out of range")));
2945+
28912946
result->day = span1->day - span2->day;
2947+
if (!SAMESIGN(span1->day, span2->day) &&
2948+
!SAMESIGN(result->day, span1->day))
2949+
ereport(ERROR,
2950+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2951+
errmsg("interval out of range")));
2952+
28922953
result->time = span1->time - span2->time;
2954+
if (!SAMESIGN(span1->time, span2->time) &&
2955+
!SAMESIGN(result->time, span1->time))
2956+
ereport(ERROR,
2957+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2958+
errmsg("interval out of range")));
2959+
28932960

28942961
PG_RETURN_INTERVAL_P(result);
28952962
}
@@ -2906,15 +2973,27 @@ interval_mul(PG_FUNCTION_ARGS)
29062973
Interval *span = PG_GETARG_INTERVAL_P(0);
29072974
float8 factor = PG_GETARG_FLOAT8(1);
29082975
double month_remainder_days,
2909-
sec_remainder;
2976+
sec_remainder,
2977+
result_double;
29102978
int32 orig_month = span->month,
29112979
orig_day = span->day;
29122980
Interval *result;
29132981

29142982
result = (Interval *) palloc(sizeof(Interval));
29152983

2916-
result->month = (int32) (span->month * factor);
2917-
result->day = (int32) (span->day * factor);
2984+
result_double = span->month * factor;
2985+
if (result_double > INT_MAX || result_double < INT_MIN)
2986+
ereport(ERROR,
2987+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2988+
errmsg("interval out of range")));
2989+
result->month = (int32) result_double;
2990+
2991+
result_double = span->day * factor;
2992+
if (result_double > INT_MAX || result_double < INT_MIN)
2993+
ereport(ERROR,
2994+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2995+
errmsg("interval out of range")));
2996+
result->day = (int32) result_double;
29182997

29192998
/*
29202999
* The above correctly handles the whole-number part of the month and day
@@ -2954,7 +3033,12 @@ interval_mul(PG_FUNCTION_ARGS)
29543033
/* cascade units down */
29553034
result->day += (int32) month_remainder_days;
29563035
#ifdef HAVE_INT64_TIMESTAMP
2957-
result->time = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
3036+
result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
3037+
if (result_double > INT64_MAX || result_double < INT64_MIN)
3038+
ereport(ERROR,
3039+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3040+
errmsg("interval out of range")));
3041+
result->time = (int64) result_double;
29583042
#else
29593043
result->time = span->time * factor + sec_remainder;
29603044
#endif

src/interfaces/ecpg/pgtypeslib/interval.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,9 @@ interval2tm(interval span, struct tm * tm, fsec_t *fsec)
10361036
static int
10371037
tm2interval(struct tm * tm, fsec_t fsec, interval * span)
10381038
{
1039+
if ((double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon > INT_MAX ||
1040+
(double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon < INT_MIN)
1041+
return -1;
10391042
span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
10401043
#ifdef HAVE_INT64_TIMESTAMP
10411044
span->time = (((((((tm->tm_mday * INT64CONST(24)) +

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy