diff --git a/src/backend/access/gist/README b/src/backend/access/gist/README
index 4bcac1f2c795d07874bb2f6da0413e4d684e5496..dd4c9fa70a028a894c73eac1917c2cc9c3d4f97e 100644
--- a/src/backend/access/gist/README
+++ b/src/backend/access/gist/README
@@ -135,7 +135,7 @@ that didn't need to be split.
 
 This differs from the insertion algorithm in the original paper. In the
 original paper, you first walk down the tree until you reach a leaf page, and
-then you adjust the downlink in the parent, and propagating the adjustment up,
+then you adjust the downlink in the parent, and propagate the adjustment up,
 all the way up to the root in the worst case. But we adjust the downlinks to
 cover the new key already when we walk down, so that when we reach the leaf
 page, we don't need to update the parents anymore, except to insert the
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b0711052fccf91c0e5cb0c01c90d9ab19cbd90ef..64125d519538746f2de41e9ed6d683ef9a730644 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -220,6 +220,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 		GistNSN		oldnsn = 0;
 		SplitedPageLayout rootpg;
 		bool		is_rootsplit;
+		int			npage;
 
 		is_rootsplit = (blkno == GIST_ROOT_BLKNO);
 
@@ -240,6 +241,19 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 		itvec = gistjoinvector(itvec, &tlen, itup, ntup);
 		dist = gistSplit(rel, page, itvec, tlen, giststate);
 
+		/*
+		 * Check that split didn't produce too many pages.
+		 */
+		npage = 0;
+		for (ptr = dist; ptr; ptr = ptr->next)
+			npage++;
+		/* in a root split, we'll add one more page to the list below */
+		if (is_rootsplit)
+			npage++;
+		if (npage > GIST_MAX_SPLIT_PAGES)
+			elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
+				 npage, GIST_MAX_SPLIT_PAGES);
+
 		/*
 		 * Set up pages to work with. Allocate new buffers for all but the
 		 * leftmost page. The original page becomes the new leftmost page, and
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
index d6fdc04cf28eb30d0937d8b8937340c9d8a01cd1..7cc64aeb490c626650b043afd2366648cb1eee9d 100644
--- a/src/backend/access/gist/gistxlog.c
+++ b/src/backend/access/gist/gistxlog.c
@@ -379,7 +379,7 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
 			  BlockNumber origrlink, GistNSN orignsn,
 			  Buffer leftchildbuf, bool markfollowright)
 {
-	XLogRecData *rdata;
+	XLogRecData rdata[GIST_MAX_SPLIT_PAGES * 2 + 2];
 	gistxlogPageSplit xlrec;
 	SplitedPageLayout *ptr;
 	int			npage = 0,
@@ -388,8 +388,12 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
 
 	for (ptr = dist; ptr; ptr = ptr->next)
 		npage++;
-
-	rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (npage * 2 + 2));
+	/*
+	 * the caller should've checked this already, but doesn't hurt to check
+	 * again.
+	 */
+	if (npage > GIST_MAX_SPLIT_PAGES)
+		elog(ERROR, "GiST page split into too many halves");
 
 	xlrec.node = node;
 	xlrec.origblkno = blkno;
@@ -439,7 +443,6 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
 
 	recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT, rdata);
 
-	pfree(rdata);
 	return recptr;
 }
 
@@ -462,14 +465,12 @@ gistXLogUpdate(RelFileNode node, Buffer buffer,
 			   IndexTuple *itup, int ituplen,
 			   Buffer leftchildbuf)
 {
-	XLogRecData *rdata;
+	XLogRecData rdata[MaxIndexTuplesPerPage + 3];
 	gistxlogPageUpdate xlrec;
 	int			cur,
 				i;
 	XLogRecPtr	recptr;
 
-	rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (3 + ituplen));
-
 	xlrec.node = node;
 	xlrec.blkno = BufferGetBlockNumber(buffer);
 	xlrec.ntodelete = ntodelete;
@@ -516,6 +517,5 @@ gistXLogUpdate(RelFileNode node, Buffer buffer,
 
 	recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
 
-	pfree(rdata);
 	return recptr;
 }
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 278c510f47ab32d9e3073bf46d1159473cc1a867..089c67942138614ce4ac42dd9513ac0f7704d15d 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -22,6 +22,21 @@
 #include "utils/rbtree.h"
 #include "utils/hsearch.h"
 
+/*
+ * Maximum number of "halves" a page can be split into in one operation.
+ * Typically a split produces 2 halves, but can be more if keys have very
+ * different lengths, or when inserting multiple keys in one operation (as
+ * when inserting downlinks to an internal node).  There is no theoretical
+ * limit on this, but in practice if you get more than a handful page halves
+ * in one split, there's something wrong with the opclass implementation.
+ * GIST_MAX_SPLIT_PAGES is an arbitrary limit on that, used to size some
+ * local arrays used during split.  Note that there is also a limit on the
+ * number of buffers that can be held locked at a time, MAX_SIMUL_LWLOCKS,
+ * so if you raise this higher than that limit, you'll just get a different
+ * error.
+ */
+#define GIST_MAX_SPLIT_PAGES		75
+
 /* Buffer lock modes */
 #define GIST_SHARE	BUFFER_LOCK_SHARE
 #define GIST_EXCLUSIVE	BUFFER_LOCK_EXCLUSIVE