From 723c16770af3a183b5df8cdea9f4b9432ddc4ea3 Mon Sep 17 00:00:00 2001
From: Sunil Seetharama <sunilfeb26@gmail.com>
Date: Fri, 17 Oct 2025 10:51:42 +0530
Subject: [PATCH v2] BRIN: Prevent integer overflow during index summarization on very large tables
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Previously, `heapBlk` was defined as an unsigned 32-bit integer. When incremented
by `pagesPerRange` on very large tables, it could wrap around, causing the condition
`heapBlk < nblocks` to remain true indefinitely — resulting in an infinite loop.

This could cause the PostgreSQL backend to hang, consuming 100% CPU indefinitely
and preventing operations from completing on large tables.

The solution is straightforward — the data type of `heapBlk` has been changed
from a 32-bit integer to a 64-bit `BlockNumber` (int64), ensuring it can safely
handle extremely large tables without risk of overflow.

This was explained very nicely by Tomas Vondra[1] and below two solutions were
suggested.
	i)  Change to int64
	ii) Tracking the prevHeapBlk

Among these two I feel using solution #1 would be more feasible(similar to previously used solution #2), though
other solution also works.This is also similar to logic used in table_block_parallelscan_nextpage()

Reference:
[1] https://www.postgresql.org/message-id/b8a4e04c-c091-056c-a379-11d35c7b2d8d%40enterprisedb.com
[2] https://github.com/postgres/postgres/commit/4bc6fb57f774ea18187fd8565aad9994160bfc17
---
 src/backend/access/brin/brin.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 7ff7467e462..d3fef042f04 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -573,7 +573,6 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 	Relation	heapRel;
 	BrinOpaque *opaque;
 	BlockNumber nblocks;
-	BlockNumber heapBlk;
 	int64		totalpages = 0;
 	FmgrInfo   *consistentFn;
 	MemoryContext oldcxt;
@@ -736,8 +735,15 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 	 * Now scan the revmap.  We start by querying for heap page 0,
 	 * incrementing by the number of pages per range; this gives us a full
 	 * view of the table.
+	 *
+	 * Since heapBlk is incremented by opaque->bo_pagesPerRange, it can exceed
+	 * the maximum 32-bit limit (2^32) on very large tables, potentially causing
+	 * the loop to become infinite.
+	 *
+	 * To prevent this overflow, the counter must use a 64-bit type, ensuring it
+	 * can handle cases where nblocks approaches 2^32.
 	 */
-	for (heapBlk = 0; heapBlk < nblocks; heapBlk += opaque->bo_pagesPerRange)
+	for (uint64 heapBlk = 0; heapBlk < nblocks; heapBlk += opaque->bo_pagesPerRange)
 	{
 		bool		addrange;
 		bool		gottuple = false;
@@ -749,7 +755,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 
 		MemoryContextReset(perRangeCxt);
 
-		tup = brinGetTupleForHeapBlock(opaque->bo_rmAccess, heapBlk, &buf,
+		tup = brinGetTupleForHeapBlock(opaque->bo_rmAccess, (BlockNumber )heapBlk, &buf,
 									   &off, &size, BUFFER_LOCK_SHARE);
 		if (tup)
 		{
@@ -926,7 +932,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 		{
 			BlockNumber pageno;
 
-			for (pageno = heapBlk;
+			for (pageno = (BlockNumber)heapBlk;
 				 pageno <= Min(nblocks, heapBlk + opaque->bo_pagesPerRange) - 1;
 				 pageno++)
 			{
-- 
2.50.1

