diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2023-12-19 11:12:16 -0500 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2023-12-19 11:12:16 -0500 |
| commit | 7e1ce2b3de16dfbe1598cef060dfc8458522938a (patch) | |
| tree | a0a5fe0eda7b6791d808f16ded3ae0b1af1175c8 /src/backend/optimizer/path | |
| parent | 2a607fb822a2ad8f3a2cc714871283ad8cdf71c4 (diff) | |
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
Diffstat (limited to 'src/backend/optimizer/path')
| -rw-r--r-- | src/backend/optimizer/path/costsize.c | 43 |
1 files changed, 35 insertions, 8 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 5227346aeb1..22635d29270 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -219,6 +219,35 @@ clamp_row_est(double nrows) } /* + * clamp_width_est + * Force a tuple-width estimate to a sane value. + * + * The planner represents datatype width and tuple width estimates as int32. + * When summing column width estimates to create a tuple width estimate, + * it's possible to reach integer overflow in edge cases. To ensure sane + * behavior, we form such sums in int64 arithmetic and then apply this routine + * to clamp to int32 range. + */ +int32 +clamp_width_est(int64 tuple_width) +{ + /* + * Anything more than MaxAllocSize is clearly bogus, since we could not + * create a tuple that large. + */ + if (tuple_width > MaxAllocSize) + return (int32) MaxAllocSize; + + /* + * Unlike clamp_row_est, we just Assert that the value isn't negative, + * rather than masking such errors. + */ + Assert(tuple_width >= 0); + + return (int32) tuple_width; +} + +/* * clamp_cardinality_to_long * Cast a Cardinality value to a sane long value. */ @@ -6101,7 +6130,7 @@ static void set_rel_width(PlannerInfo *root, RelOptInfo *rel) { Oid reloid = planner_rt_fetch(rel->relid, root)->relid; - int32 tuple_width = 0; + int64 tuple_width = 0; bool have_wholerow_var = false; ListCell *lc; @@ -6213,7 +6242,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel) */ if (have_wholerow_var) { - int32 wholerow_width = MAXALIGN(SizeofHeapTupleHeader); + int64 wholerow_width = MAXALIGN(SizeofHeapTupleHeader); if (reloid != InvalidOid) { @@ -6230,7 +6259,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel) wholerow_width += rel->attr_widths[i - rel->min_attr]; } - rel->attr_widths[0 - rel->min_attr] = wholerow_width; + rel->attr_widths[0 - rel->min_attr] = clamp_width_est(wholerow_width); /* * Include the whole-row Var as part of the output tuple. Yes, that @@ -6239,8 +6268,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel) tuple_width += wholerow_width; } - Assert(tuple_width >= 0); - rel->reltarget->width = tuple_width; + rel->reltarget->width = clamp_width_est(tuple_width); } /* @@ -6258,7 +6286,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel) PathTarget * set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target) { - int32 tuple_width = 0; + int64 tuple_width = 0; ListCell *lc; /* Vars are assumed to have cost zero, but other exprs do not */ @@ -6282,8 +6310,7 @@ set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target) } } - Assert(tuple_width >= 0); - target->width = tuple_width; + target->width = clamp_width_est(tuple_width); return target; } |
