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

Commit72d0c13

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 parent3b67289 commit72d0c13

File tree

3 files changed

+69
-21
lines changed

3 files changed

+69
-21
lines changed

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

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3315,19 +3315,13 @@ interval_mul(PG_FUNCTION_ARGS)
33153315
result= (Interval*)palloc(sizeof(Interval));
33163316

33173317
result_double=span->month*factor;
3318-
if (isnan(result_double)||
3319-
result_double>INT_MAX||result_double<INT_MIN)
3320-
ereport(ERROR,
3321-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3322-
errmsg("interval out of range")));
3318+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3319+
gotoout_of_range;
33233320
result->month= (int32)result_double;
33243321

33253322
result_double=span->day*factor;
3326-
if (isnan(result_double)||
3327-
result_double>INT_MAX||result_double<INT_MIN)
3328-
ereport(ERROR,
3329-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3330-
errmsg("interval out of range")));
3323+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3324+
gotoout_of_range;
33313325
result->day= (int32)result_double;
33323326

33333327
/*
@@ -3361,20 +3355,30 @@ interval_mul(PG_FUNCTION_ARGS)
33613355
*/
33623356
if (fabs(sec_remainder) >=SECS_PER_DAY)
33633357
{
3364-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3358+
if (pg_add_s32_overflow(result->day,
3359+
(int) (sec_remainder /SECS_PER_DAY),
3360+
&result->day))
3361+
gotoout_of_range;
33653362
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
33663363
}
33673364

33683365
/* cascade units down */
3369-
result->day+= (int32)month_remainder_days;
3366+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3367+
&result->day))
3368+
gotoout_of_range;
33703369
result_double=rint(span->time*factor+sec_remainder*USECS_PER_SEC);
33713370
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3372-
ereport(ERROR,
3373-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3374-
errmsg("interval out of range")));
3371+
gotoout_of_range;
33753372
result->time= (int64)result_double;
33763373

33773374
PG_RETURN_INTERVAL_P(result);
3375+
3376+
out_of_range:
3377+
ereport(ERROR,
3378+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3379+
errmsg("interval out of range"));
3380+
3381+
PG_RETURN_NULL();/* keep compiler quiet */
33783382
}
33793383

33803384
Datum
@@ -3393,7 +3397,8 @@ interval_div(PG_FUNCTION_ARGS)
33933397
Interval*span=PG_GETARG_INTERVAL_P(0);
33943398
float8factor=PG_GETARG_FLOAT8(1);
33953399
doublemonth_remainder_days,
3396-
sec_remainder;
3400+
sec_remainder,
3401+
result_double;
33973402
int32orig_month=span->month,
33983403
orig_day=span->day;
33993404
Interval*result;
@@ -3405,8 +3410,15 @@ interval_div(PG_FUNCTION_ARGS)
34053410
(errcode(ERRCODE_DIVISION_BY_ZERO),
34063411
errmsg("division by zero")));
34073412

3408-
result->month= (int32) (span->month /factor);
3409-
result->day= (int32) (span->day /factor);
3413+
result_double=span->month /factor;
3414+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3415+
gotoout_of_range;
3416+
result->month= (int32)result_double;
3417+
3418+
result_double=span->day /factor;
3419+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3420+
gotoout_of_range;
3421+
result->day= (int32)result_double;
34103422

34113423
/*
34123424
* Fractional months full days into days. See comment in interval_mul().
@@ -3418,15 +3430,30 @@ interval_div(PG_FUNCTION_ARGS)
34183430
sec_remainder=TSROUND(sec_remainder);
34193431
if (fabs(sec_remainder) >=SECS_PER_DAY)
34203432
{
3421-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3433+
if (pg_add_s32_overflow(result->day,
3434+
(int) (sec_remainder /SECS_PER_DAY),
3435+
&result->day))
3436+
gotoout_of_range;
34223437
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
34233438
}
34243439

34253440
/* cascade units down */
3426-
result->day+= (int32)month_remainder_days;
3427-
result->time=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3441+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3442+
&result->day))
3443+
gotoout_of_range;
3444+
result_double=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3445+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3446+
gotoout_of_range;
3447+
result->time= (int64)result_double;
34283448

34293449
PG_RETURN_INTERVAL_P(result);
3450+
3451+
out_of_range:
3452+
ereport(ERROR,
3453+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3454+
errmsg("interval out of range"));
3455+
3456+
PG_RETURN_NULL();/* keep compiler quiet */
34303457
}
34313458

34323459

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,19 @@ SELECT * FROM INTERVAL_TBL;
388388
@ 5 mons 12 hours
389389
(10 rows)
390390

391+
-- multiplication and division overflow test cases
392+
SELECT '3000000 months'::interval * 1000;
393+
ERROR: interval out of range
394+
SELECT '3000000 months'::interval / 0.001;
395+
ERROR: interval out of range
396+
SELECT '3000000 days'::interval * 1000;
397+
ERROR: interval out of range
398+
SELECT '3000000 days'::interval / 0.001;
399+
ERROR: interval out of range
400+
SELECT '1 month 2146410 days'::interval * 1000.5002;
401+
ERROR: interval out of range
402+
SELECT '4611686018427387904 usec'::interval / 0.1;
403+
ERROR: interval out of range
391404
-- test avg(interval), which is somewhat fragile since people have been
392405
-- known to change the allowed input syntax for type interval without
393406
-- updating pg_aggregate.agginitval

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

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

137137
SELECT*FROM INTERVAL_TBL;
138138

139+
-- multiplication and division overflow test cases
140+
SELECT'3000000 months'::interval*1000;
141+
SELECT'3000000 months'::interval/0.001;
142+
SELECT'3000000 days'::interval*1000;
143+
SELECT'3000000 days'::interval/0.001;
144+
SELECT'1 month 2146410 days'::interval*1000.5002;
145+
SELECT'4611686018427387904 usec'::interval/0.1;
146+
139147
-- test avg(interval), which is somewhat fragile since people have been
140148
-- known to change the allowed input syntax for type interval without
141149
-- updating pg_aggregate.agginitval

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp