Make the overflow guards in ExecChooseHashTableSize be more protective.

The original coding ensured nbuckets and nbatch didn't exceed INT_MAX,
which while not insane on its own terms did nothing to protect subsequent
code like "palloc(nbatch * sizeof(BufFile *))".  Since enormous join size
estimates might well be planner error rather than reality, it seems best
to constrain the initial sizes to be not more than work_mem/sizeof(pointer),
thus ensuring the allocated arrays don't exceed work_mem.  We will allow
nbatch to get bigger than that during subsequent ExecHashIncreaseNumBatches
calls, but we should still guard against integer overflow in those palloc
requests.  Per bug #5145 from Bernt Marius Johnsen.

Although the given test case only seems to fail back to 8.2, previous
releases have variants of this issue, so patch all supported branches.
REL8_5_ALPHA3_BRANCH
Tom Lane 16 years ago
parent 90412225d2
commit 8442317beb
  1. 43
      src/backend/executor/nodeHash.c

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.122 2009/09/27 21:10:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.123 2009/10/30 20:58:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -394,6 +394,7 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
double inner_rel_bytes; double inner_rel_bytes;
long hash_table_bytes; long hash_table_bytes;
long skew_table_bytes; long skew_table_bytes;
long max_pointers;
int nbatch; int nbatch;
int nbuckets; int nbuckets;
int i; int i;
@ -435,17 +436,18 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
{ {
skew_table_bytes = hash_table_bytes * SKEW_WORK_MEM_PERCENT / 100; skew_table_bytes = hash_table_bytes * SKEW_WORK_MEM_PERCENT / 100;
*num_skew_mcvs = skew_table_bytes / ( /*----------
/* size of a hash tuple */ * Divisor is:
tupsize + * size of a hash tuple +
/* worst-case size of skewBucket[] per MCV */ * worst-case size of skewBucket[] per MCV +
* size of skewBucketNums[] entry +
* size of skew bucket struct itself
*----------
*/
*num_skew_mcvs = skew_table_bytes / (tupsize +
(8 * sizeof(HashSkewBucket *)) + (8 * sizeof(HashSkewBucket *)) +
/* size of skewBucketNums[] entry */
sizeof(int) + sizeof(int) +
/* size of skew bucket struct itself */ SKEW_BUCKET_OVERHEAD);
SKEW_BUCKET_OVERHEAD
);
if (*num_skew_mcvs > 0) if (*num_skew_mcvs > 0)
hash_table_bytes -= skew_table_bytes; hash_table_bytes -= skew_table_bytes;
} }
@ -455,8 +457,13 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
/* /*
* Set nbuckets to achieve an average bucket load of NTUP_PER_BUCKET when * Set nbuckets to achieve an average bucket load of NTUP_PER_BUCKET when
* memory is filled. Set nbatch to the smallest power of 2 that appears * memory is filled. Set nbatch to the smallest power of 2 that appears
* sufficient. * sufficient. The Min() steps limit the results so that the pointer
* arrays we'll try to allocate do not exceed work_mem.
*/ */
max_pointers = (work_mem * 1024L) / sizeof(void *);
/* also ensure we avoid integer overflow in nbatch and nbuckets */
max_pointers = Min(max_pointers, INT_MAX / 2);
if (inner_rel_bytes > hash_table_bytes) if (inner_rel_bytes > hash_table_bytes)
{ {
/* We'll need multiple batches */ /* We'll need multiple batches */
@ -465,11 +472,11 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
int minbatch; int minbatch;
lbuckets = (hash_table_bytes / tupsize) / NTUP_PER_BUCKET; lbuckets = (hash_table_bytes / tupsize) / NTUP_PER_BUCKET;
lbuckets = Min(lbuckets, INT_MAX / 2); lbuckets = Min(lbuckets, max_pointers);
nbuckets = (int) lbuckets; nbuckets = (int) lbuckets;
dbatch = ceil(inner_rel_bytes / hash_table_bytes); dbatch = ceil(inner_rel_bytes / hash_table_bytes);
dbatch = Min(dbatch, INT_MAX / 2); dbatch = Min(dbatch, max_pointers);
minbatch = (int) dbatch; minbatch = (int) dbatch;
nbatch = 2; nbatch = 2;
while (nbatch < minbatch) while (nbatch < minbatch)
@ -481,7 +488,7 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
double dbuckets; double dbuckets;
dbuckets = ceil(ntuples / NTUP_PER_BUCKET); dbuckets = ceil(ntuples / NTUP_PER_BUCKET);
dbuckets = Min(dbuckets, INT_MAX / 2); dbuckets = Min(dbuckets, max_pointers);
nbuckets = (int) dbuckets; nbuckets = (int) dbuckets;
nbatch = 1; nbatch = 1;
@ -555,7 +562,7 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
return; return;
/* safety check to avoid overflow */ /* safety check to avoid overflow */
if (oldnbatch > INT_MAX / 2) if (oldnbatch > Min(INT_MAX / 2, MaxAllocSize / (sizeof(void *) * 2)))
return; return;
nbatch = oldnbatch * 2; nbatch = oldnbatch * 2;
@ -1033,9 +1040,9 @@ ExecHashBuildSkewHash(HashJoinTable hashtable, Hash *node, int mcvsToUse)
* will be at least one null entry, so searches will always * will be at least one null entry, so searches will always
* terminate.) * terminate.)
* *
* Note: this code could fail if mcvsToUse exceeds INT_MAX/8, but that * Note: this code could fail if mcvsToUse exceeds INT_MAX/8 or
* is not currently possible since we limit pg_statistic entries to * MaxAllocSize/sizeof(void *)/8, but that is not currently possible
* much less than that. * since we limit pg_statistic entries to much less than that.
*/ */
nbuckets = 2; nbuckets = 2;
while (nbuckets <= mcvsToUse) while (nbuckets <= mcvsToUse)

Loading…
Cancel
Save