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

Commit428770a

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 parent10912f7 commit428770a

File tree

3 files changed

+70
-21
lines changed

3 files changed

+70
-21
lines changed

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

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include"access/xact.h"
2424
#include"catalog/pg_type.h"
25+
#include"common/int.h"
2526
#include"common/int128.h"
2627
#include"funcapi.h"
2728
#include"libpq/pqformat.h"
@@ -3184,19 +3185,13 @@ interval_mul(PG_FUNCTION_ARGS)
31843185
result= (Interval*)palloc(sizeof(Interval));
31853186

31863187
result_double=span->month*factor;
3187-
if (isnan(result_double)||
3188-
result_double>INT_MAX||result_double<INT_MIN)
3189-
ereport(ERROR,
3190-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3191-
errmsg("interval out of range")));
3188+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3189+
gotoout_of_range;
31923190
result->month= (int32)result_double;
31933191

31943192
result_double=span->day*factor;
3195-
if (isnan(result_double)||
3196-
result_double>INT_MAX||result_double<INT_MIN)
3197-
ereport(ERROR,
3198-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3199-
errmsg("interval out of range")));
3193+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3194+
gotoout_of_range;
32003195
result->day= (int32)result_double;
32013196

32023197
/*
@@ -3230,20 +3225,30 @@ interval_mul(PG_FUNCTION_ARGS)
32303225
*/
32313226
if (Abs(sec_remainder) >=SECS_PER_DAY)
32323227
{
3233-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3228+
if (pg_add_s32_overflow(result->day,
3229+
(int) (sec_remainder /SECS_PER_DAY),
3230+
&result->day))
3231+
gotoout_of_range;
32343232
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
32353233
}
32363234

32373235
/* cascade units down */
3238-
result->day+= (int32)month_remainder_days;
3236+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3237+
&result->day))
3238+
gotoout_of_range;
32393239
result_double=rint(span->time*factor+sec_remainder*USECS_PER_SEC);
32403240
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3241-
ereport(ERROR,
3242-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3243-
errmsg("interval out of range")));
3241+
gotoout_of_range;
32443242
result->time= (int64)result_double;
32453243

32463244
PG_RETURN_INTERVAL_P(result);
3245+
3246+
out_of_range:
3247+
ereport(ERROR,
3248+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3249+
errmsg("interval out of range"));
3250+
3251+
PG_RETURN_NULL();/* keep compiler quiet */
32473252
}
32483253

32493254
Datum
@@ -3262,7 +3267,8 @@ interval_div(PG_FUNCTION_ARGS)
32623267
Interval*span=PG_GETARG_INTERVAL_P(0);
32633268
float8factor=PG_GETARG_FLOAT8(1);
32643269
doublemonth_remainder_days,
3265-
sec_remainder;
3270+
sec_remainder,
3271+
result_double;
32663272
int32orig_month=span->month,
32673273
orig_day=span->day;
32683274
Interval*result;
@@ -3274,8 +3280,15 @@ interval_div(PG_FUNCTION_ARGS)
32743280
(errcode(ERRCODE_DIVISION_BY_ZERO),
32753281
errmsg("division by zero")));
32763282

3277-
result->month= (int32) (span->month /factor);
3278-
result->day= (int32) (span->day /factor);
3283+
result_double=span->month /factor;
3284+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3285+
gotoout_of_range;
3286+
result->month= (int32)result_double;
3287+
3288+
result_double=span->day /factor;
3289+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT32(result_double))
3290+
gotoout_of_range;
3291+
result->day= (int32)result_double;
32793292

32803293
/*
32813294
* Fractional months full days into days. See comment in interval_mul().
@@ -3287,15 +3300,30 @@ interval_div(PG_FUNCTION_ARGS)
32873300
sec_remainder=TSROUND(sec_remainder);
32883301
if (Abs(sec_remainder) >=SECS_PER_DAY)
32893302
{
3290-
result->day+= (int) (sec_remainder /SECS_PER_DAY);
3303+
if (pg_add_s32_overflow(result->day,
3304+
(int) (sec_remainder /SECS_PER_DAY),
3305+
&result->day))
3306+
gotoout_of_range;
32913307
sec_remainder-= (int) (sec_remainder /SECS_PER_DAY)*SECS_PER_DAY;
32923308
}
32933309

32943310
/* cascade units down */
3295-
result->day+= (int32)month_remainder_days;
3296-
result->time=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3311+
if (pg_add_s32_overflow(result->day, (int32)month_remainder_days,
3312+
&result->day))
3313+
gotoout_of_range;
3314+
result_double=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
3315+
if (isnan(result_double)|| !FLOAT8_FITS_IN_INT64(result_double))
3316+
gotoout_of_range;
3317+
result->time= (int64)result_double;
32973318

32983319
PG_RETURN_INTERVAL_P(result);
3320+
3321+
out_of_range:
3322+
ereport(ERROR,
3323+
errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3324+
errmsg("interval out of range"));
3325+
3326+
PG_RETURN_NULL();/* keep compiler quiet */
32993327
}
33003328

33013329

‎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 '' AS ten, * 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''AS ten,*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