From 723c16770af3a183b5df8cdea9f4b9432ddc4ea3 Mon Sep 17 00:00:00 2001 From: Sunil Seetharama 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