Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb218fbb

Browse files
committed
Guard against overflow in interval_mul() and interval_div().
Commits146604e anda898b40 added overflow checks tointerval_mul(), but not to interval_div(), which contains almostidentical code, and so is susceptible to the same kinds ofoverflows. In addition, those checks did not catch all possibleoverflow conditions.Add additional checks to the "cascade down" code in interval_mul(),and copy all the overflow checks over to the corresponding code ininterval_div(), so that they both generate "interval out of range"errors, rather than returning bogus results.Given that these errors are relatively easy to hit, back-patch to allsupported branches.Per bug #18200 from Alexander Lakhin, and subsequent investigation.Discussion:https://postgr.es/m/18200-5ea288c7b2d504b1%40postgresql.org
1 parent4bc8f29 commitb218fbb

File tree

3 files changed

+80
-44
lines changed

3 files changed

+80
-44
lines changed

‎src/backend/utils/adt/timestamp.c

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3548,17 +3548,14 @@ interval_mul(PG_FUNCTION_ARGS)
35483548
* interval type has nothing equivalent to NaN.
35493549
*/
35503550
if (isnan(factor))
3551-
ereport(ERROR,
3552-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3553-
errmsg("interval out of range")));
3551+
gotoout_of_range;
35543552

35553553
if (INTERVAL_NOT_FINITE(span))
35563554
{
35573555
if (factor==0.0)
3558-
ereport(ERROR,
3559-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3560-
errmsg("interval out of range")));
3561-
elseif (factor<0.0)
3556+
gotoout_of_range;
3557+
3558+
if (factor<0.0)
35623559
interval_um_internal(span,result);
35633560
else
35643561
memcpy(result,span,sizeof(Interval));
@@ -3570,10 +3567,9 @@ interval_mul(PG_FUNCTION_ARGS)
35703567
intisign=interval_sign(span);
35713568

35723569
if (isign==0)
3573-
ereport(ERROR,
3574-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3575-
errmsg("interval out of range")));
3576-
elseif (factor*isign<0)
3570+
gotoout_of_range;
3571+
3572+
if (factor*isign<0)
35773573
INTERVAL_NOBEGIN(result);
35783574
else
35793575
INTERVAL_NOEND(result);
@@ -3582,19 +3578,13 @@ interval_mul(PG_FUNCTION_ARGS)
35823578
}
35833579

35843580
result_double=span->month*factor;
3585-
if (isnan(result_double)||
3586-
result_double>INT_MAX||result_double<INT_MIN)
3587-
ereport(ERROR,
3588-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3589-
errmsg("interval out of range")));
3581+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3582+
gotoout_of_range;
35903583
result->month= (int32)result_double;
35913584

35923585
result_double=span->day*factor;
3593-
if (isnan(result_double)||
3594-
result_double>INT_MAX||result_double<INT_MIN)
3595-
ereport(ERROR,
3596-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3597-
errmsg("interval out of range")));
3586+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3587+
gotoout_of_range;
35983588
result->day= (int32)result_double;
35993589

36003590
/*
@@ -3628,25 +3618,33 @@ interval_mul(PG_FUNCTION_ARGS)
36283618
*/
36293619
if (fabs(sec_remainder) >=SECS_PER_DAY)
36303620
{
3631-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3621+
if (pg_add_s32_overflow(result->day,
3622+
(int) (sec_remainder /SECS_PER_DAY),
3623+
&result->day))
3624+
gotoout_of_range;
36323625
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
36333626
}
36343627

36353628
/* cascade units down */
3636-
result->day+= (int32)month_remainder_days;
3629+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3630+
&result->day))
3631+
gotoout_of_range;
36373632
result_double=rint(span->time*factor+sec_remainder*USECS_PER_SEC);
36383633
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3639-
ereport(ERROR,
3640-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3641-
errmsg("interval out of range")));
3634+
gotoout_of_range;
36423635
result->time= (int64)result_double;
36433636

36443637
if (INTERVAL_NOT_FINITE(result))
3645-
ereport(ERROR,
3646-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3647-
errmsg("interval out of range")));
3638+
gotoout_of_range;
36483639

36493640
PG_RETURN_INTERVAL_P(result);
3641+
3642+
out_of_range:
3643+
ereport(ERROR,
3644+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3645+
errmsg("interval out of range"));
3646+
3647+
PG_RETURN_NULL();/* keep compiler quiet */
36503648
}
36513649

36523650
Datum
@@ -3665,7 +3663,8 @@ interval_div(PG_FUNCTION_ARGS)
36653663
Interval*span=PG_GETARG_INTERVAL_P(0);
36663664
float8factor=PG_GETARG_FLOAT8(1);
36673665
doublemonth_remainder_days,
3668-
sec_remainder;
3666+
sec_remainder,
3667+
result_double;
36693668
int32orig_month=span->month,
36703669
orig_day=span->day;
36713670
Interval*result;
@@ -3685,16 +3684,12 @@ interval_div(PG_FUNCTION_ARGS)
36853684
* by the regular division code, causing all fields to be set to zero.
36863685
*/
36873686
if (isnan(factor))
3688-
ereport(ERROR,
3689-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3690-
errmsg("interval out of range")));
3687+
gotoout_of_range;
36913688

36923689
if (INTERVAL_NOT_FINITE(span))
36933690
{
36943691
if (isinf(factor))
3695-
ereport(ERROR,
3696-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3697-
errmsg("interval out of range")));
3692+
gotoout_of_range;
36983693

36993694
if (factor<0.0)
37003695
interval_um_internal(span,result);
@@ -3704,8 +3699,15 @@ interval_div(PG_FUNCTION_ARGS)
37043699
PG_RETURN_INTERVAL_P(result);
37053700
}
37063701

3707-
result->month= (int32) (span->month /factor);
3708-
result->day= (int32) (span->day /factor);
3702+
result_double=span->month /factor;
3703+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3704+
gotoout_of_range;
3705+
result->month= (int32)result_double;
3706+
3707+
result_double=span->day /factor;
3708+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3709+
gotoout_of_range;
3710+
result->day= (int32)result_double;
37093711

37103712
/*
37113713
* Fractional months full days into days. See comment in interval_mul().
@@ -3717,20 +3719,33 @@ interval_div(PG_FUNCTION_ARGS)
37173719
sec_remainder=TSROUND(sec_remainder);
37183720
if (fabs(sec_remainder) >=SECS_PER_DAY)
37193721
{
3720-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3722+
if (pg_add_s32_overflow(result->day,
3723+
(int) (sec_remainder /SECS_PER_DAY),
3724+
&result->day))
3725+
gotoout_of_range;
37213726
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
37223727
}
37233728

37243729
/* cascade units down */
3725-
result->day+= (int32)month_remainder_days;
3726-
result->time=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3730+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3731+
&result->day))
3732+
gotoout_of_range;
3733+
result_double=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3734+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3735+
gotoout_of_range;
3736+
result->time= (int64)result_double;
37273737

37283738
if (INTERVAL_NOT_FINITE(result))
3729-
ereport(ERROR,
3730-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3731-
errmsg("interval out of range")));
3739+
gotoout_of_range;
37323740

37333741
PG_RETURN_INTERVAL_P(result);
3742+
3743+
out_of_range:
3744+
ereport(ERROR,
3745+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3746+
errmsg("interval out of range"));
3747+
3748+
PG_RETURN_NULL();/* keep compiler quiet */
37343749
}
37353750

37363751

‎src/test/regress/expected/interval.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,19 @@ SELECT * FROM INTERVAL_TBL;
488488
-infinity
489489
(12 rows)
490490

491+
-- multiplication and division overflow test cases
492+
SELECT '3000000 months'::interval * 1000;
493+
ERROR: interval out of range
494+
SELECT '3000000 months'::interval / 0.001;
495+
ERROR: interval out of range
496+
SELECT '3000000 days'::interval * 1000;
497+
ERROR: interval out of range
498+
SELECT '3000000 days'::interval / 0.001;
499+
ERROR: interval out of range
500+
SELECT '1 month 2146410 days'::interval * 1000.5002;
501+
ERROR: interval out of range
502+
SELECT '4611686018427387904 usec'::interval / 0.1;
503+
ERROR: interval out of range
491504
-- test avg(interval), which is somewhat fragile since people have been
492505
-- known to change the allowed input syntax for type interval without
493506
-- updating pg_aggregate.agginitval

‎src/test/regress/sql/interval.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ SET IntervalStyle to postgres_verbose;
154154

155155
SELECT*FROM INTERVAL_TBL;
156156

157+
-- multiplication and division overflow test cases
158+
SELECT'3000000 months'::interval*1000;
159+
SELECT'3000000 months'::interval/0.001;
160+
SELECT'3000000 days'::interval*1000;
161+
SELECT'3000000 days'::interval/0.001;
162+
SELECT'1 month 2146410 days'::interval*1000.5002;
163+
SELECT'4611686018427387904 usec'::interval/0.1;
164+
157165
-- test avg(interval), which is somewhat fragile since people have been
158166
-- known to change the allowed input syntax for type interval without
159167
-- updating pg_aggregate.agginitval

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp