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

Commit2851aa7

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 parentff77285 commit2851aa7

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
@@ -3246,19 +3246,13 @@ interval_mul(PG_FUNCTION_ARGS)
32463246
result= (Interval*)palloc(sizeof(Interval));
32473247

32483248
result_double=span->month*factor;
3249-
if (isnan(result_double)||
3250-
result_double>INT_MAX||result_double<INT_MIN)
3251-
ereport(ERROR,
3252-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3253-
errmsg("interval out of range")));
3249+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3250+
gotoout_of_range;
32543251
result->month= (int32)result_double;
32553252

32563253
result_double=span->day*factor;
3257-
if (isnan(result_double)||
3258-
result_double>INT_MAX||result_double<INT_MIN)
3259-
ereport(ERROR,
3260-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3261-
errmsg("interval out of range")));
3254+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3255+
gotoout_of_range;
32623256
result->day= (int32)result_double;
32633257

32643258
/*
@@ -3292,20 +3286,30 @@ interval_mul(PG_FUNCTION_ARGS)
32923286
*/
32933287
if (Abs(sec_remainder) >=SECS_PER_DAY)
32943288
{
3295-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3289+
if (pg_add_s32_overflow(result->day,
3290+
(int) (sec_remainder /SECS_PER_DAY),
3291+
&result->day))
3292+
gotoout_of_range;
32963293
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
32973294
}
32983295

32993296
/* cascade units down */
3300-
result->day+= (int32)month_remainder_days;
3297+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3298+
&result->day))
3299+
gotoout_of_range;
33013300
result_double=rint(span->time*factor+sec_remainder*USECS_PER_SEC);
33023301
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3303-
ereport(ERROR,
3304-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3305-
errmsg("interval out of range")));
3302+
gotoout_of_range;
33063303
result->time= (int64)result_double;
33073304

33083305
PG_RETURN_INTERVAL_P(result);
3306+
3307+
out_of_range:
3308+
ereport(ERROR,
3309+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3310+
errmsg("interval out of range"));
3311+
3312+
PG_RETURN_NULL();/* keep compiler quiet */
33093313
}
33103314

33113315
Datum
@@ -3324,7 +3328,8 @@ interval_div(PG_FUNCTION_ARGS)
33243328
Interval*span=PG_GETARG_INTERVAL_P(0);
33253329
float8factor=PG_GETARG_FLOAT8(1);
33263330
doublemonth_remainder_days,
3327-
sec_remainder;
3331+
sec_remainder,
3332+
result_double;
33283333
int32orig_month=span->month,
33293334
orig_day=span->day;
33303335
Interval*result;
@@ -3336,8 +3341,15 @@ interval_div(PG_FUNCTION_ARGS)
33363341
(errcode(ERRCODE_DIVISION_BY_ZERO),
33373342
errmsg("division by zero")));
33383343

3339-
result->month= (int32) (span->month /factor);
3340-
result->day= (int32) (span->day /factor);
3344+
result_double=span->month /factor;
3345+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3346+
gotoout_of_range;
3347+
result->month= (int32)result_double;
3348+
3349+
result_double=span->day /factor;
3350+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3351+
gotoout_of_range;
3352+
result->day= (int32)result_double;
33413353

33423354
/*
33433355
* Fractional months full days into days. See comment in interval_mul().
@@ -3349,15 +3361,30 @@ interval_div(PG_FUNCTION_ARGS)
33493361
sec_remainder=TSROUND(sec_remainder);
33503362
if (Abs(sec_remainder) >=SECS_PER_DAY)
33513363
{
3352-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3364+
if (pg_add_s32_overflow(result->day,
3365+
(int) (sec_remainder /SECS_PER_DAY),
3366+
&result->day))
3367+
gotoout_of_range;
33533368
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
33543369
}
33553370

33563371
/* cascade units down */
3357-
result->day+= (int32)month_remainder_days;
3358-
result->time=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3372+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3373+
&result->day))
3374+
gotoout_of_range;
3375+
result_double=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3376+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3377+
gotoout_of_range;
3378+
result->time= (int64)result_double;
33593379

33603380
PG_RETURN_INTERVAL_P(result);
3381+
3382+
out_of_range:
3383+
ereport(ERROR,
3384+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3385+
errmsg("interval out of range"));
3386+
3387+
PG_RETURN_NULL();/* keep compiler quiet */
33613388
}
33623389

33633390

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

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

360+
-- multiplication and division overflow test cases
361+
SELECT '3000000 months'::interval * 1000;
362+
ERROR: interval out of range
363+
SELECT '3000000 months'::interval / 0.001;
364+
ERROR: interval out of range
365+
SELECT '3000000 days'::interval * 1000;
366+
ERROR: interval out of range
367+
SELECT '3000000 days'::interval / 0.001;
368+
ERROR: interval out of range
369+
SELECT '1 month 2146410 days'::interval * 1000.5002;
370+
ERROR: interval out of range
371+
SELECT '4611686018427387904 usec'::interval / 0.1;
372+
ERROR: interval out of range
360373
-- test avg(interval), which is somewhat fragile since people have been
361374
-- known to change the allowed input syntax for type interval without
362375
-- updating pg_aggregate.agginitval

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

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

130130
SELECT*FROM INTERVAL_TBL;
131131

132+
-- multiplication and division overflow test cases
133+
SELECT'3000000 months'::interval*1000;
134+
SELECT'3000000 months'::interval/0.001;
135+
SELECT'3000000 days'::interval*1000;
136+
SELECT'3000000 days'::interval/0.001;
137+
SELECT'1 month 2146410 days'::interval*1000.5002;
138+
SELECT'4611686018427387904 usec'::interval/0.1;
139+
132140
-- test avg(interval), which is somewhat fragile since people have been
133141
-- known to change the allowed input syntax for type interval without
134142
-- updating pg_aggregate.agginitval

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp