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

Commit2ffcebd

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 parent75a20a4 commit2ffcebd

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

31923192
result_double=span->month*factor;
3193-
if (isnan(result_double)||
3194-
result_double>INT_MAX||result_double<INT_MIN)
3195-
ereport(ERROR,
3196-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3197-
errmsg("interval out of range")));
3193+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3194+
gotoout_of_range;
31983195
result->month= (int32)result_double;
31993196

32003197
result_double=span->day*factor;
3201-
if (isnan(result_double)||
3202-
result_double>INT_MAX||result_double<INT_MIN)
3203-
ereport(ERROR,
3204-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3205-
errmsg("interval out of range")));
3198+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3199+
gotoout_of_range;
32063200
result->day= (int32)result_double;
32073201

32083202
/*
@@ -3236,20 +3230,30 @@ interval_mul(PG_FUNCTION_ARGS)
32363230
*/
32373231
if (Abs(sec_remainder) >=SECS_PER_DAY)
32383232
{
3239-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3233+
if (pg_add_s32_overflow(result->day,
3234+
(int) (sec_remainder /SECS_PER_DAY),
3235+
&result->day))
3236+
gotoout_of_range;
32403237
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
32413238
}
32423239

32433240
/* cascade units down */
3244-
result->day+= (int32)month_remainder_days;
3241+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3242+
&result->day))
3243+
gotoout_of_range;
32453244
result_double=rint(span->time*factor+sec_remainder*USECS_PER_SEC);
32463245
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3247-
ereport(ERROR,
3248-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3249-
errmsg("interval out of range")));
3246+
gotoout_of_range;
32503247
result->time= (int64)result_double;
32513248

32523249
PG_RETURN_INTERVAL_P(result);
3250+
3251+
out_of_range:
3252+
ereport(ERROR,
3253+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3254+
errmsg("interval out of range"));
3255+
3256+
PG_RETURN_NULL();/* keep compiler quiet */
32533257
}
32543258

32553259
Datum
@@ -3268,7 +3272,8 @@ interval_div(PG_FUNCTION_ARGS)
32683272
Interval*span=PG_GETARG_INTERVAL_P(0);
32693273
float8factor=PG_GETARG_FLOAT8(1);
32703274
doublemonth_remainder_days,
3271-
sec_remainder;
3275+
sec_remainder,
3276+
result_double;
32723277
int32orig_month=span->month,
32733278
orig_day=span->day;
32743279
Interval*result;
@@ -3280,8 +3285,15 @@ interval_div(PG_FUNCTION_ARGS)
32803285
(errcode(ERRCODE_DIVISION_BY_ZERO),
32813286
errmsg("division by zero")));
32823287

3283-
result->month= (int32) (span->month /factor);
3284-
result->day= (int32) (span->day /factor);
3288+
result_double=span->month /factor;
3289+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3290+
gotoout_of_range;
3291+
result->month= (int32)result_double;
3292+
3293+
result_double=span->day /factor;
3294+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3295+
gotoout_of_range;
3296+
result->day= (int32)result_double;
32853297

32863298
/*
32873299
* Fractional months full days into days. See comment in interval_mul().
@@ -3293,15 +3305,30 @@ interval_div(PG_FUNCTION_ARGS)
32933305
sec_remainder=TSROUND(sec_remainder);
32943306
if (Abs(sec_remainder) >=SECS_PER_DAY)
32953307
{
3296-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3308+
if (pg_add_s32_overflow(result->day,
3309+
(int) (sec_remainder /SECS_PER_DAY),
3310+
&result->day))
3311+
gotoout_of_range;
32973312
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
32983313
}
32993314

33003315
/* cascade units down */
3301-
result->day+= (int32)month_remainder_days;
3302-
result->time=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3316+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3317+
&result->day))
3318+
gotoout_of_range;
3319+
result_double=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3320+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3321+
gotoout_of_range;
3322+
result->time= (int64)result_double;
33033323

33043324
PG_RETURN_INTERVAL_P(result);
3325+
3326+
out_of_range:
3327+
ereport(ERROR,
3328+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3329+
errmsg("interval out of range"));
3330+
3331+
PG_RETURN_NULL();/* keep compiler quiet */
33053332
}
33063333

33073334

‎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 make_interval(0, 0, 0, 0, 0, 0, 4611686018427.387904) / 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 make_interval(0,0,0,0,0,0,4611686018427.387904)/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