Skip to content

Commit 05a5623

Browse files
committed
Avoid palloc in critical section in GiST WAL-logging.
Memory allocation can fail if you run out of memory, and inside a critical section that will lead to a PANIC. Use conservatively-sized arrays in stack instead. There was previously no explicit limit on the number of pages a GiST split can produce, it was only limited by the number of LWLocks that can be held simultaneously (100 at the moment). This patch adds an explicit limit of 75 pages. That should be plenty, a typical split shouldn't produce more than 2-3 page halves. The bug has been there forever, but only backpatch down to 9.1. The code was changed significantly in 9.1, and it doesn't seem worth the risk or trouble to adapt this for 9.0 and 8.4.
1 parent b7a4243 commit 05a5623

File tree

4 files changed

+38
-9
lines changed

4 files changed

+38
-9
lines changed

src/backend/access/gist/README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ that didn't need to be split.
128128

129129
This differs from the insertion algorithm in the original paper. In the
130130
original paper, you first walk down the tree until you reach a leaf page, and
131-
then you adjust the downlink in the parent, and propagating the adjustment up,
131+
then you adjust the downlink in the parent, and propagate the adjustment up,
132132
all the way up to the root in the worst case. But we adjust the downlinks to
133133
cover the new key already when we walk down, so that when we reach the leaf
134134
page, we don't need to update the parents anymore, except to insert the

src/backend/access/gist/gist.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate,
356356
SplitedPageLayout rootpg;
357357
BlockNumber blkno = BufferGetBlockNumber(buffer);
358358
bool is_rootsplit;
359+
int npage;
359360

360361
is_rootsplit = (blkno == GIST_ROOT_BLKNO);
361362

@@ -376,6 +377,19 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate,
376377
itvec = gistjoinvector(itvec, &tlen, itup, ntup);
377378
dist = gistSplit(state->r, page, itvec, tlen, giststate);
378379

380+
/*
381+
* Check that split didn't produce too many pages.
382+
*/
383+
npage = 0;
384+
for (ptr = dist; ptr; ptr = ptr->next)
385+
npage++;
386+
/* in a root split, we'll add one more page to the list below */
387+
if (is_rootsplit)
388+
npage++;
389+
if (npage > GIST_MAX_SPLIT_PAGES)
390+
elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
391+
npage, GIST_MAX_SPLIT_PAGES);
392+
379393
/*
380394
* Set up pages to work with. Allocate new buffers for all but the
381395
* leftmost page. The original page becomes the new leftmost page, and

src/backend/access/gist/gistxlog.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
434434
BlockNumber origrlink, GistNSN orignsn,
435435
Buffer leftchildbuf)
436436
{
437-
XLogRecData *rdata;
437+
XLogRecData rdata[GIST_MAX_SPLIT_PAGES * 2 + 2];
438438
gistxlogPageSplit xlrec;
439439
SplitedPageLayout *ptr;
440440
int npage = 0,
@@ -443,8 +443,12 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
443443

444444
for (ptr = dist; ptr; ptr = ptr->next)
445445
npage++;
446-
447-
rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (npage * 2 + 2));
446+
/*
447+
* the caller should've checked this already, but doesn't hurt to check
448+
* again.
449+
*/
450+
if (npage > GIST_MAX_SPLIT_PAGES)
451+
elog(ERROR, "GiST page split into too many halves");
448452

449453
xlrec.node = node;
450454
xlrec.origblkno = blkno;
@@ -493,7 +497,6 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
493497

494498
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT, rdata);
495499

496-
pfree(rdata);
497500
return recptr;
498501
}
499502

@@ -516,14 +519,12 @@ gistXLogUpdate(RelFileNode node, Buffer buffer,
516519
IndexTuple *itup, int ituplen,
517520
Buffer leftchildbuf)
518521
{
519-
XLogRecData *rdata;
522+
XLogRecData rdata[MaxIndexTuplesPerPage + 3];
520523
gistxlogPageUpdate xlrec;
521524
int cur,
522525
i;
523526
XLogRecPtr recptr;
524527

525-
rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (3 + ituplen));
526-
527528
xlrec.node = node;
528529
xlrec.blkno = BufferGetBlockNumber(buffer);
529530
xlrec.ntodelete = ntodelete;
@@ -570,6 +571,5 @@ gistXLogUpdate(RelFileNode node, Buffer buffer,
570571

571572
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
572573

573-
pfree(rdata);
574574
return recptr;
575575
}

src/include/access/gist_private.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@
1919
#include "storage/bufmgr.h"
2020
#include "utils/rbtree.h"
2121

22+
/*
23+
* Maximum number of "halves" a page can be split into in one operation.
24+
* Typically a split produces 2 halves, but can be more if keys have very
25+
* different lengths, or when inserting multiple keys in one operation (as
26+
* when inserting downlinks to an internal node). There is no theoretical
27+
* limit on this, but in practice if you get more than a handful page halves
28+
* in one split, there's something wrong with the opclass implementation.
29+
* GIST_MAX_SPLIT_PAGES is an arbitrary limit on that, used to size some
30+
* local arrays used during split. Note that there is also a limit on the
31+
* number of buffers that can be held locked at a time, MAX_SIMUL_LWLOCKS,
32+
* so if you raise this higher than that limit, you'll just get a different
33+
* error.
34+
*/
35+
#define GIST_MAX_SPLIT_PAGES 75
36+
2237
/* Buffer lock modes */
2338
#define GIST_SHARE BUFFER_LOCK_SHARE
2439
#define GIST_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy