Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a916cb9

Browse files
committed
Avoid overflow hazard when clamping group counts to "long int".
Several places in the planner tried to clamp a double value to fit in a "long" by doing (long) Min(x, (double) LONG_MAX); This is subtly incorrect, because it casts LONG_MAX to double and potentially back again. If long is 64 bits then the double value is inexact, and the platform might round it up to LONG_MAX+1 resulting in an overflow and an undesirably negative output. While it's not hard to rewrite the expression into a safe form, let's put it into a common function to reduce the risk of someone doing it wrong in future. In principle this is a bug fix, but since the problem could only manifest with group count estimates exceeding 2^63, it seems unlikely that anyone has actually hit this or will do so anytime soon. We're fixing it mainly to satisfy fuzzer-type tools. That being the case, a HEAD-only fix seems sufficient. Andrey Lepikhov Discussion: https://postgr.es/m/[email protected]
1 parent ac1ae47 commit a916cb9

File tree

4 files changed

+33
-6
lines changed

4 files changed

+33
-6
lines changed

src/backend/executor/nodeSubplan.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
*/
2727
#include "postgres.h"
2828

29-
#include <limits.h>
3029
#include <math.h>
3130

3231
#include "access/htup_details.h"
@@ -35,6 +34,7 @@
3534
#include "miscadmin.h"
3635
#include "nodes/makefuncs.h"
3736
#include "nodes/nodeFuncs.h"
37+
#include "optimizer/optimizer.h"
3838
#include "utils/array.h"
3939
#include "utils/lsyscache.h"
4040
#include "utils/memutils.h"
@@ -498,7 +498,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
498498
node->havehashrows = false;
499499
node->havenullrows = false;
500500

501-
nbuckets = (long) Min(planstate->plan->plan_rows, (double) LONG_MAX);
501+
nbuckets = clamp_cardinality_to_long(planstate->plan->plan_rows);
502502
if (nbuckets < 1)
503503
nbuckets = 1;
504504

src/backend/optimizer/path/costsize.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171

7272
#include "postgres.h"
7373

74+
#include <limits.h>
7475
#include <math.h>
7576

7677
#include "access/amapi.h"
@@ -215,6 +216,32 @@ clamp_row_est(double nrows)
215216
return nrows;
216217
}
217218

219+
/*
220+
* clamp_cardinality_to_long
221+
* Cast a Cardinality value to a sane long value.
222+
*/
223+
long
224+
clamp_cardinality_to_long(Cardinality x)
225+
{
226+
/*
227+
* Just for paranoia's sake, ensure we do something sane with negative or
228+
* NaN values.
229+
*/
230+
if (isnan(x))
231+
return LONG_MAX;
232+
if (x <= 0)
233+
return 0;
234+
235+
/*
236+
* If "long" is 64 bits, then LONG_MAX cannot be represented exactly as a
237+
* double. Casting it to double and back may well result in overflow due
238+
* to rounding, so avoid doing that. We trust that any double value that
239+
* compares strictly less than "(double) LONG_MAX" will cast to a
240+
* representable "long" value.
241+
*/
242+
return (x < (double) LONG_MAX) ? (long) x : LONG_MAX;
243+
}
244+
218245

219246
/*
220247
* cost_seqscan

src/backend/optimizer/plan/createplan.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
#include "postgres.h"
1818

19-
#include <limits.h>
2019
#include <math.h>
2120

2221
#include "access/sysattr.h"
@@ -2724,7 +2723,7 @@ create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags)
27242723
flags | CP_LABEL_TLIST);
27252724

27262725
/* Convert numGroups to long int --- but 'ware overflow! */
2727-
numGroups = (long) Min(best_path->numGroups, (double) LONG_MAX);
2726+
numGroups = clamp_cardinality_to_long(best_path->numGroups);
27282727

27292728
plan = make_setop(best_path->cmd,
27302729
best_path->strategy,
@@ -2761,7 +2760,7 @@ create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path)
27612760
tlist = build_path_tlist(root, &best_path->path);
27622761

27632762
/* Convert numGroups to long int --- but 'ware overflow! */
2764-
numGroups = (long) Min(best_path->numGroups, (double) LONG_MAX);
2763+
numGroups = clamp_cardinality_to_long(best_path->numGroups);
27652764

27662765
plan = make_recursive_union(tlist,
27672766
leftplan,
@@ -6554,7 +6553,7 @@ make_agg(List *tlist, List *qual,
65546553
long numGroups;
65556554

65566555
/* Reduce to long, but 'ware overflow! */
6557-
numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
6556+
numGroups = clamp_cardinality_to_long(dNumGroups);
65586557

65596558
node->aggstrategy = aggstrategy;
65606559
node->aggsplit = aggsplit;

src/include/optimizer/optimizer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ extern PGDLLIMPORT double recursive_worktable_factor;
9595
extern PGDLLIMPORT int effective_cache_size;
9696

9797
extern double clamp_row_est(double nrows);
98+
extern long clamp_cardinality_to_long(Cardinality x);
9899

99100
/* in path/indxpath.c: */
100101

0 commit comments

Comments
 (0)