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
This commit is contained in:
parent
571addd729
commit
146604ec43
@ -1587,7 +1587,7 @@ SELECT E'\\xDEADBEEF';
|
|||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry><type>interval [ <replaceable>fields</replaceable> ] [ (<replaceable>p</replaceable>) ]</type></entry>
|
<entry><type>interval [ <replaceable>fields</replaceable> ] [ (<replaceable>p</replaceable>) ]</type></entry>
|
||||||
<entry>12 bytes</entry>
|
<entry>16 bytes</entry>
|
||||||
<entry>time interval</entry>
|
<entry>time interval</entry>
|
||||||
<entry>-178000000 years</entry>
|
<entry>-178000000 years</entry>
|
||||||
<entry>178000000 years</entry>
|
<entry>178000000 years</entry>
|
||||||
|
@ -2976,6 +2976,9 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
|
|||||||
type = DTK_MONTH;
|
type = DTK_MONTH;
|
||||||
if (*field[i] == '-')
|
if (*field[i] == '-')
|
||||||
val2 = -val2;
|
val2 = -val2;
|
||||||
|
if (((double)val * MONTHS_PER_YEAR + val2) > INT_MAX ||
|
||||||
|
((double)val * MONTHS_PER_YEAR + val2) < INT_MIN)
|
||||||
|
return DTERR_FIELD_OVERFLOW;
|
||||||
val = val * MONTHS_PER_YEAR + val2;
|
val = val * MONTHS_PER_YEAR + val2;
|
||||||
fval = 0;
|
fval = 0;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,15 @@
|
|||||||
#error -ffast-math is known to break this code
|
#error -ffast-math is known to break this code
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
|
||||||
|
|
||||||
|
#ifndef INT64_MAX
|
||||||
|
#define INT64_MAX INT64CONST(0x7FFFFFFFFFFFFFFF)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INT64_MIN
|
||||||
|
#define INT64_MIN (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Set at postmaster start */
|
/* Set at postmaster start */
|
||||||
TimestampTz PgStartTime;
|
TimestampTz PgStartTime;
|
||||||
@ -1694,7 +1703,11 @@ interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
|
|||||||
#ifdef HAVE_INT64_TIMESTAMP
|
#ifdef HAVE_INT64_TIMESTAMP
|
||||||
tfrac = time / USECS_PER_HOUR;
|
tfrac = time / USECS_PER_HOUR;
|
||||||
time -= tfrac * USECS_PER_HOUR;
|
time -= tfrac * USECS_PER_HOUR;
|
||||||
tm->tm_hour = tfrac; /* could overflow ... */
|
tm->tm_hour = tfrac;
|
||||||
|
if (!SAMESIGN(tm->tm_hour, tfrac))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
tfrac = time / USECS_PER_MINUTE;
|
tfrac = time / USECS_PER_MINUTE;
|
||||||
time -= tfrac * USECS_PER_MINUTE;
|
time -= tfrac * USECS_PER_MINUTE;
|
||||||
tm->tm_min = tfrac;
|
tm->tm_min = tfrac;
|
||||||
@ -1725,7 +1738,11 @@ recalc:
|
|||||||
int
|
int
|
||||||
tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
|
tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
|
||||||
{
|
{
|
||||||
span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
|
double total_months = (double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
|
||||||
|
|
||||||
|
if (total_months > INT_MAX || total_months < INT_MIN)
|
||||||
|
return -1;
|
||||||
|
span->month = total_months;
|
||||||
span->day = tm->tm_mday;
|
span->day = tm->tm_mday;
|
||||||
#ifdef HAVE_INT64_TIMESTAMP
|
#ifdef HAVE_INT64_TIMESTAMP
|
||||||
span->time = (((((tm->tm_hour * INT64CONST(60)) +
|
span->time = (((((tm->tm_hour * INT64CONST(60)) +
|
||||||
@ -2826,8 +2843,21 @@ interval_um(PG_FUNCTION_ARGS)
|
|||||||
result = (Interval *) palloc(sizeof(Interval));
|
result = (Interval *) palloc(sizeof(Interval));
|
||||||
|
|
||||||
result->time = -interval->time;
|
result->time = -interval->time;
|
||||||
|
/* overflow check copied from int4um */
|
||||||
|
if (interval->time != 0 && SAMESIGN(result->time, interval->time))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
result->day = -interval->day;
|
result->day = -interval->day;
|
||||||
|
if (interval->day != 0 && SAMESIGN(result->day, interval->day))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
result->month = -interval->month;
|
result->month = -interval->month;
|
||||||
|
if (interval->month != 0 && SAMESIGN(result->month, interval->month))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
PG_RETURN_INTERVAL_P(result);
|
PG_RETURN_INTERVAL_P(result);
|
||||||
}
|
}
|
||||||
@ -2872,8 +2902,26 @@ interval_pl(PG_FUNCTION_ARGS)
|
|||||||
result = (Interval *) palloc(sizeof(Interval));
|
result = (Interval *) palloc(sizeof(Interval));
|
||||||
|
|
||||||
result->month = span1->month + span2->month;
|
result->month = span1->month + span2->month;
|
||||||
|
/* overflow check copied from int4pl */
|
||||||
|
if (SAMESIGN(span1->month, span2->month) &&
|
||||||
|
!SAMESIGN(result->month, span1->month))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
result->day = span1->day + span2->day;
|
result->day = span1->day + span2->day;
|
||||||
|
if (SAMESIGN(span1->day, span2->day) &&
|
||||||
|
!SAMESIGN(result->day, span1->day))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
result->time = span1->time + span2->time;
|
result->time = span1->time + span2->time;
|
||||||
|
if (SAMESIGN(span1->time, span2->time) &&
|
||||||
|
!SAMESIGN(result->time, span1->time))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
PG_RETURN_INTERVAL_P(result);
|
PG_RETURN_INTERVAL_P(result);
|
||||||
}
|
}
|
||||||
@ -2888,8 +2936,27 @@ interval_mi(PG_FUNCTION_ARGS)
|
|||||||
result = (Interval *) palloc(sizeof(Interval));
|
result = (Interval *) palloc(sizeof(Interval));
|
||||||
|
|
||||||
result->month = span1->month - span2->month;
|
result->month = span1->month - span2->month;
|
||||||
|
/* overflow check copied from int4mi */
|
||||||
|
if (!SAMESIGN(span1->month, span2->month) &&
|
||||||
|
!SAMESIGN(result->month, span1->month))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
result->day = span1->day - span2->day;
|
result->day = span1->day - span2->day;
|
||||||
|
if (!SAMESIGN(span1->day, span2->day) &&
|
||||||
|
!SAMESIGN(result->day, span1->day))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
result->time = span1->time - span2->time;
|
result->time = span1->time - span2->time;
|
||||||
|
if (!SAMESIGN(span1->time, span2->time) &&
|
||||||
|
!SAMESIGN(result->time, span1->time))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
|
|
||||||
PG_RETURN_INTERVAL_P(result);
|
PG_RETURN_INTERVAL_P(result);
|
||||||
}
|
}
|
||||||
@ -2906,15 +2973,27 @@ interval_mul(PG_FUNCTION_ARGS)
|
|||||||
Interval *span = PG_GETARG_INTERVAL_P(0);
|
Interval *span = PG_GETARG_INTERVAL_P(0);
|
||||||
float8 factor = PG_GETARG_FLOAT8(1);
|
float8 factor = PG_GETARG_FLOAT8(1);
|
||||||
double month_remainder_days,
|
double month_remainder_days,
|
||||||
sec_remainder;
|
sec_remainder,
|
||||||
|
result_double;
|
||||||
int32 orig_month = span->month,
|
int32 orig_month = span->month,
|
||||||
orig_day = span->day;
|
orig_day = span->day;
|
||||||
Interval *result;
|
Interval *result;
|
||||||
|
|
||||||
result = (Interval *) palloc(sizeof(Interval));
|
result = (Interval *) palloc(sizeof(Interval));
|
||||||
|
|
||||||
result->month = (int32) (span->month * factor);
|
result_double = span->month * factor;
|
||||||
result->day = (int32) (span->day * factor);
|
if (result_double > INT_MAX || result_double < INT_MIN)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
result->month = (int32) result_double;
|
||||||
|
|
||||||
|
result_double = span->day * factor;
|
||||||
|
if (result_double > INT_MAX || result_double < INT_MIN)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
result->day = (int32) result_double;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The above correctly handles the whole-number part of the month and day
|
* The above correctly handles the whole-number part of the month and day
|
||||||
@ -2954,7 +3033,12 @@ interval_mul(PG_FUNCTION_ARGS)
|
|||||||
/* cascade units down */
|
/* cascade units down */
|
||||||
result->day += (int32) month_remainder_days;
|
result->day += (int32) month_remainder_days;
|
||||||
#ifdef HAVE_INT64_TIMESTAMP
|
#ifdef HAVE_INT64_TIMESTAMP
|
||||||
result->time = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
|
result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
|
||||||
|
if (result_double > INT64_MAX || result_double < INT64_MIN)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
result->time = (int64) result_double;
|
||||||
#else
|
#else
|
||||||
result->time = span->time * factor + sec_remainder;
|
result->time = span->time * factor + sec_remainder;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1036,6 +1036,9 @@ recalc:
|
|||||||
static int
|
static int
|
||||||
tm2interval(struct tm * tm, fsec_t fsec, interval * span)
|
tm2interval(struct tm * tm, fsec_t fsec, interval * span)
|
||||||
{
|
{
|
||||||
|
if ((double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon > INT_MAX ||
|
||||||
|
(double)tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon < INT_MIN)
|
||||||
|
return -1;
|
||||||
span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
|
span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
|
||||||
#ifdef HAVE_INT64_TIMESTAMP
|
#ifdef HAVE_INT64_TIMESTAMP
|
||||||
span->time = (((((((tm->tm_mday * INT64CONST(24)) +
|
span->time = (((((((tm->tm_mday * INT64CONST(24)) +
|
||||||
|
Loading…
Reference in New Issue
Block a user