Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7e1ce2b

Browse files
committed
Prevent integer overflow when forming tuple width estimates.
It's at least theoretically possible to overflow int32 when adding up column width estimates to make a row width estimate. (The bug example isn't terribly convincing as a real use-case, but perhaps wide joins would provide a more plausible route to trouble.) This'd lead to assertion failures or silly planner behavior. To forestall it, make the relevant functions compute their running sums in int64 arithmetic and then clamp to int32 range at the end. We can reasonably assume that MaxAllocSize is a hard limit on actual tuple width, so clamping to that is simply a correction for dubious input values, and there's no need to go as far as widening width variables to int64 everywhere. Per bug #18247 from RekGRpth. There've been no reports of this issue arising in practical cases, so I feel no need to back-patch. Richard Guo and Tom Lane Discussion: https://postgr.es/m/18247-11ac477f02954422@postgresql.org
1 parent 2a607fb commit 7e1ce2b

File tree

6 files changed

+52
-15
lines changed

6 files changed

+52
-15
lines changed

src/backend/optimizer/path/costsize.c

+35-8
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,35 @@ clamp_row_est(double nrows)
218218
return nrows;
219219
}
220220

221+
/*
222+
* clamp_width_est
223+
* Force a tuple-width estimate to a sane value.
224+
*
225+
* The planner represents datatype width and tuple width estimates as int32.
226+
* When summing column width estimates to create a tuple width estimate,
227+
* it's possible to reach integer overflow in edge cases. To ensure sane
228+
* behavior, we form such sums in int64 arithmetic and then apply this routine
229+
* to clamp to int32 range.
230+
*/
231+
int32
232+
clamp_width_est(int64 tuple_width)
233+
{
234+
/*
235+
* Anything more than MaxAllocSize is clearly bogus, since we could not
236+
* create a tuple that large.
237+
*/
238+
if (tuple_width > MaxAllocSize)
239+
return (int32) MaxAllocSize;
240+
241+
/*
242+
* Unlike clamp_row_est, we just Assert that the value isn't negative,
243+
* rather than masking such errors.
244+
*/
245+
Assert(tuple_width >= 0);
246+
247+
return (int32) tuple_width;
248+
}
249+
221250
/*
222251
* clamp_cardinality_to_long
223252
* Cast a Cardinality value to a sane long value.
@@ -6101,7 +6130,7 @@ static void
61016130
set_rel_width(PlannerInfo *root, RelOptInfo *rel)
61026131
{
61036132
Oid reloid = planner_rt_fetch(rel->relid, root)->relid;
6104-
int32 tuple_width = 0;
6133+
int64 tuple_width = 0;
61056134
bool have_wholerow_var = false;
61066135
ListCell *lc;
61076136

@@ -6213,7 +6242,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
62136242
*/
62146243
if (have_wholerow_var)
62156244
{
6216-
int32 wholerow_width = MAXALIGN(SizeofHeapTupleHeader);
6245+
int64 wholerow_width = MAXALIGN(SizeofHeapTupleHeader);
62176246

62186247
if (reloid != InvalidOid)
62196248
{
@@ -6230,7 +6259,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
62306259
wholerow_width += rel->attr_widths[i - rel->min_attr];
62316260
}
62326261

6233-
rel->attr_widths[0 - rel->min_attr] = wholerow_width;
6262+
rel->attr_widths[0 - rel->min_attr] = clamp_width_est(wholerow_width);
62346263

62356264
/*
62366265
* Include the whole-row Var as part of the output tuple. Yes, that
@@ -6239,8 +6268,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
62396268
tuple_width += wholerow_width;
62406269
}
62416270

6242-
Assert(tuple_width >= 0);
6243-
rel->reltarget->width = tuple_width;
6271+
rel->reltarget->width = clamp_width_est(tuple_width);
62446272
}
62456273

62466274
/*
@@ -6258,7 +6286,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
62586286
PathTarget *
62596287
set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target)
62606288
{
6261-
int32 tuple_width = 0;
6289+
int64 tuple_width = 0;
62626290
ListCell *lc;
62636291

62646292
/* Vars are assumed to have cost zero, but other exprs do not */
@@ -6282,8 +6310,7 @@ set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target)
62826310
}
62836311
}
62846312

6285-
Assert(tuple_width >= 0);
6286-
target->width = tuple_width;
6313+
target->width = clamp_width_est(tuple_width);
62876314

62886315
return target;
62896316
}

src/backend/optimizer/plan/planner.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -4610,6 +4610,7 @@ create_one_window_path(PlannerInfo *root,
46104610
* Note: a WindowFunc adds nothing to the target's eval costs; but
46114611
* we do need to account for the increase in tlist width.
46124612
*/
4613+
int64 tuple_width = window_target->width;
46134614
ListCell *lc2;
46144615

46154616
window_target = copy_pathtarget(window_target);
@@ -4618,8 +4619,9 @@ create_one_window_path(PlannerInfo *root,
46184619
WindowFunc *wfunc = lfirst_node(WindowFunc, lc2);
46194620

46204621
add_column_to_pathtarget(window_target, (Expr *) wfunc, 0);
4621-
window_target->width += get_typavgwidth(wfunc->wintype, -1);
4622+
tuple_width += get_typavgwidth(wfunc->wintype, -1);
46224623
}
4624+
window_target->width = clamp_width_est(tuple_width);
46234625
}
46244626
else
46254627
{

src/backend/optimizer/util/placeholder.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
375375
SpecialJoinInfo *sjinfo)
376376
{
377377
Relids relids = joinrel->relids;
378+
int64 tuple_width = joinrel->reltarget->width;
378379
ListCell *lc;
379380

380381
foreach(lc, root->placeholder_list)
@@ -419,7 +420,7 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
419420
cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
420421
joinrel->reltarget->cost.startup += cost.startup;
421422
joinrel->reltarget->cost.per_tuple += cost.per_tuple;
422-
joinrel->reltarget->width += phinfo->ph_width;
423+
tuple_width += phinfo->ph_width;
423424
}
424425
}
425426

@@ -443,6 +444,8 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
443444
phinfo->ph_lateral);
444445
}
445446
}
447+
448+
joinrel->reltarget->width = clamp_width_est(tuple_width);
446449
}
447450

448451
/*

src/backend/optimizer/util/plancat.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,7 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
11371137
int32
11381138
get_rel_data_width(Relation rel, int32 *attr_widths)
11391139
{
1140-
int32 tuple_width = 0;
1140+
int64 tuple_width = 0;
11411141
int i;
11421142

11431143
for (i = 1; i <= RelationGetNumberOfAttributes(rel); i++)
@@ -1167,7 +1167,7 @@ get_rel_data_width(Relation rel, int32 *attr_widths)
11671167
tuple_width += item_width;
11681168
}
11691169

1170-
return tuple_width;
1170+
return clamp_width_est(tuple_width);
11711171
}
11721172

11731173
/*

src/backend/optimizer/util/relnode.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "optimizer/clauses.h"
2323
#include "optimizer/cost.h"
2424
#include "optimizer/inherit.h"
25+
#include "optimizer/optimizer.h"
2526
#include "optimizer/pathnode.h"
2627
#include "optimizer/paths.h"
2728
#include "optimizer/placeholder.h"
@@ -1092,6 +1093,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
10921093
bool can_null)
10931094
{
10941095
Relids relids = joinrel->relids;
1096+
int64 tuple_width = joinrel->reltarget->width;
10951097
ListCell *vars;
10961098
ListCell *lc;
10971099

@@ -1144,7 +1146,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
11441146
joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
11451147
phv);
11461148
/* Bubbling up the precomputed result has cost zero */
1147-
joinrel->reltarget->width += phinfo->ph_width;
1149+
tuple_width += phinfo->ph_width;
11481150
}
11491151
continue;
11501152
}
@@ -1165,7 +1167,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
11651167
list_nth(root->row_identity_vars, var->varattno - 1);
11661168

11671169
/* Update reltarget width estimate from RowIdentityVarInfo */
1168-
joinrel->reltarget->width += ridinfo->rowidwidth;
1170+
tuple_width += ridinfo->rowidwidth;
11691171
}
11701172
else
11711173
{
@@ -1181,7 +1183,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
11811183
continue; /* nope, skip it */
11821184

11831185
/* Update reltarget width estimate from baserel's attr_widths */
1184-
joinrel->reltarget->width += baserel->attr_widths[ndx];
1186+
tuple_width += baserel->attr_widths[ndx];
11851187
}
11861188

11871189
/*
@@ -1221,6 +1223,8 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
12211223

12221224
/* Vars have cost zero, so no need to adjust reltarget->cost */
12231225
}
1226+
1227+
joinrel->reltarget->width = clamp_width_est(tuple_width);
12241228
}
12251229

12261230
/*

src/include/optimizer/optimizer.h

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ extern PGDLLIMPORT double recursive_worktable_factor;
9090
extern PGDLLIMPORT int effective_cache_size;
9191

9292
extern double clamp_row_est(double nrows);
93+
extern int32 clamp_width_est(int64 tuple_width);
9394
extern long clamp_cardinality_to_long(Cardinality x);
9495

9596
/* in path/indxpath.c: */

0 commit comments

Comments
 (0)