From 4b8632a93e3f38e606963e892e76bd268c1b5e21 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 26 Jun 2023 11:43:37 +1200
Subject: [PATCH v3 3/3] Fix race in SSI interaction with gin fast path.

The ginfast.c code previously checked for conflicts in before locking
the relevant buffer, leaving a window where a RW conflict could be
missed.

There was also a place where buffer ID and block number were confused
while trying to predicate-lock a page, noted by visual inspection.

Back-patch to all supported releases.

Reported-by: Dmitry Dolgov <9erthalion6@gmail.com>
Discussion: https://postgr.es/m/17949-a0f17035294a55e2%40postgresql.org
---
 src/backend/access/gin/ginfast.c | 10 ++++++++--
 src/backend/access/gin/ginget.c  |  4 +++-
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index ca7d770d86..c6524f759c 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -245,9 +245,10 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 	/*
 	 * An insertion to the pending list could logically belong anywhere in the
 	 * tree, so it conflicts with all serializable scans.  All scans acquire a
-	 * predicate lock on the metabuffer to represent that.
+	 * predicate lock on the metabuffer to represent that.  Therefore we'll
+	 * check for conflicts in, but not until we have the page locked and are
+	 * ready to modify the page.
 	 */
-	CheckForSerializableConflictIn(index, NULL, GIN_METAPAGE_BLKNO);
 
 	if (collector->sumsize + collector->ntuples * sizeof(ItemIdData) > GinListPageSize)
 	{
@@ -291,6 +292,8 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 		LockBuffer(metabuffer, GIN_EXCLUSIVE);
 		metadata = GinPageGetMeta(metapage);
 
+		CheckForSerializableConflictIn(index, NULL, GIN_METAPAGE_BLKNO);
+
 		if (metadata->head == InvalidBlockNumber)
 		{
 			/*
@@ -310,6 +313,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 		}
 		else
 		{
+
 			/*
 			 * Merge lists
 			 */
@@ -353,6 +357,8 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 		char	   *ptr;
 		char	   *collectordata;
 
+		CheckForSerializableConflictIn(index, NULL, GIN_METAPAGE_BLKNO);
+
 		buffer = ReadBuffer(index, metadata->tail);
 		LockBuffer(buffer, GIN_EXCLUSIVE);
 		page = BufferGetPage(buffer);
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index cb676a710f..1f0214498c 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -140,7 +140,9 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
 	 * Predicate lock entry leaf page, following pages will be locked by
 	 * moveRightIfItNeeded()
 	 */
-	PredicateLockPage(btree->index, stack->buffer, snapshot);
+	PredicateLockPage(btree->index,
+					  BufferGetBlockNumber(stack->buffer),
+					  snapshot);
 
 	for (;;)
 	{
-- 
2.40.1

