0001-Fix-off-by-one-stack-buffer-overflow-in-pg_surgery.patch

application/octet-stream

Filename: 0001-Fix-off-by-one-stack-buffer-overflow-in-pg_surgery.patch
Type: application/octet-stream
Part: 0
Message: Re: Fw: Re: heap_force_common in contrib/pg_surgery/heap_surgery.c has an off by one stack buffer overflow

Patch

Format: format-patch
Series: patch 0001
Subject: Fix off-by-one stack buffer overflow in pg_surgery Issue: heap_force_common() declared include_this_tid[] with MaxHeapTuplesPerPage slots and indexed it directly with a 1-based OffsetNumber, so an input TID whose offset number equals MaxHeapTuplesPerPage wrote one byte past the end of the stack array and this woulod cause a seg fault and crash postgres
File+
contrib/pg_surgery/heap_surgery.c 6 3
From b4c62d4d32683341b3a344936e8ac8ad958d9f7b Mon Sep 17 00:00:00 2001
From: spoondla <s_poondla@apple.com>
Date: Wed, 3 Jun 2026 15:17:00 -0700
Subject: [PATCH] Fix off-by-one stack buffer overflow in pg_surgery Issue:
 heap_force_common() declared include_this_tid[] with MaxHeapTuplesPerPage
 slots and indexed it directly with a 1-based OffsetNumber, so an input TID
 whose offset number equals MaxHeapTuplesPerPage wrote one byte past the end
 of the stack array and this woulod cause a seg fault and crash postgres

Fix:
Updated the size of include_this_tid[] as MaxHeapTuplesPerPage + 1 so every legal
1-based offset has a slot, and reject offno > MaxHeapTuplesPerPage in
the per-TID validation so a corrupted page header that reports an
impossibly large maxoffset cannot reach the array either.
---
 contrib/pg_surgery/heap_surgery.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/contrib/pg_surgery/heap_surgery.c b/contrib/pg_surgery/heap_surgery.c
index ae4e7c0136c..19169131815 100644
--- a/contrib/pg_surgery/heap_surgery.c
+++ b/contrib/pg_surgery/heap_surgery.c
@@ -92,7 +92,8 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
 	Relation	rel;
 	OffsetNumber curr_start_ptr,
 				next_start_ptr;
-	bool		include_this_tid[MaxHeapTuplesPerPage];
+	/* Sized +1 because OffsetNumbers are 1-based and can reach MaxHeapTuplesPerPage. */
+	bool		include_this_tid[MaxHeapTuplesPerPage + 1];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -193,7 +194,9 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
 			ItemId		itemid;
 
 			/* Check whether the offset number is valid. */
-			if (offno == InvalidOffsetNumber || offno > maxoffset)
+			if (offno == InvalidOffsetNumber ||
+				offno > maxoffset ||
+				offno > MaxHeapTuplesPerPage)
 			{
 				ereport(NOTICE,
 						errmsg("skipping tid (%u, %u) for relation \"%s\" because the item number is out of range",
@@ -228,7 +231,7 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
 			}
 
 			/* Mark it for processing. */
-			Assert(offno < MaxHeapTuplesPerPage);
+			Assert(offno <= MaxHeapTuplesPerPage);
 			include_this_tid[offno] = true;
 		}
 
-- 
2.39.5 (Apple Git-154)