Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+1-1
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

+3
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

+90-6
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

+3
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)