From 4669d64ba13f8605b9a9ac4c7e59b846c76c7556 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Fri, 18 Sep 2026 15:45:35 -0400
Subject: [PATCH 1/4] Assert correct VM page passed to pruning

Before pruning a heap page, we get the current status of the
corresponding VM page. If the passed in vmbuffer isn't the right one,
visibilitymap_get_status() will silently unpin it and pin the correct
page. Pruning assumes the caller manages the vmbuffer lifecycle, so this
would leave the caller with a stale VM reference and would leak the new
VM pin. To avoid mistakes in development, assert that the correct VM
page is pinned before beginning.
---
 src/backend/access/heap/pruneheap.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 50f810c8830..5778ba49a05 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -443,7 +443,13 @@ prune_freeze_setup(PruneFreezeParams *params,
 	prstate->buffer = params->buffer;
 	prstate->page = BufferGetPage(params->buffer);
 
-	Assert(BufferIsValid(params->vmbuffer));
+	/*
+	 * The caller must have pinned the VM page covering this heap block. If it
+	 * hadn't, visibilitymap_get_status() below would silently release the
+	 * caller's pin and take its own, leaving the caller holding a stale
+	 * buffer and leaking ours.
+	 */
+	Assert(visibilitymap_pin_ok(prstate->block, params->vmbuffer));
 	prstate->vmbuffer = params->vmbuffer;
 	prstate->new_vmbits = 0;
 	prstate->old_vmbits = visibilitymap_get_status(prstate->relation,
-- 
2.43.0

