From f965132abc3520f06dbfc8d5f89222cf00728e00 Mon Sep 17 00:00:00 2001
From: Ranier Vilela <ranier.vf@gmail.com>
Date: Sun, 11 Jul 2021 16:17:50 -0300
Subject: [PATCH] _bt_restore_page have issues can lead a memory corrupt, when
 recovery tries restore index tuples to a page.

First the maximum index tuples per page varies that system to system.
at Windows 64 bits, the MaxIndexTuplesPerPage is 408.
The int *len* parameter allows 268.435.455 index tuples per page.
2.147.483.647 / sizeof(IndexTupleData) = 268.435.455   /* IndexTupleData size is 8 bytes */

So if _bt_restore_page tries restore a file corrupted or maliciously created,
can write after array bounds.

Fix by checking and raising a error PANIC in this case.

Another issue is that _bt_restore_page can increment the pointer *from*,
to after bounds that can point.

Fix by removing the unnecessary increment.

While there, reduce scope of two variables.

Author: Ranier Vilela (ranier.vf@gmail.com)
---
 src/backend/access/nbtree/nbtxlog.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index c2e920f159..8fe80bc8ff 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -35,16 +35,19 @@ static MemoryContext opCtx;		/* working memory for operations */
  * the one with highest item number appears first (lowest on the page).
  */
 static void
-_bt_restore_page(Page page, char *from, int len)
+_bt_restore_page(Page page, char *from, Size len)
 {
-	IndexTupleData itupdata;
-	Size		itemsz;
-	char	   *end = from + len;
+	const char *end = from + len;
 	Item		items[MaxIndexTuplesPerPage];
 	uint16		itemsizes[MaxIndexTuplesPerPage];
 	int			i;
 	int			nitems;
 
+	/* Protect against corrupted recovery file */
+	nitems = (len / sizeof(IndexTupleData));
+	if (nitems < 0 || nitems > MaxIndexTuplesPerPage)
+		elog(PANIC, "_bt_restore_page: cannot restore %d items to page", nitems);
+
 	/*
 	 * To get the items back in the original order, we add them to the page in
 	 * reverse.  To figure out where one tuple ends and another begins, we
@@ -53,6 +56,9 @@ _bt_restore_page(Page page, char *from, int len)
 	i = 0;
 	while (from < end)
 	{
+		IndexTupleData	itupdata;
+		Size				itemsz;
+
 		/*
 		 * As we step through the items, 'from' won't always be properly
 		 * aligned, so we need to use memcpy().  Further, we use Item (which
@@ -73,12 +79,9 @@ _bt_restore_page(Page page, char *from, int len)
 	nitems = i;
 
 	for (i = nitems - 1; i >= 0; i--)
-	{
 		if (PageAddItem(page, items[i], itemsizes[i], nitems - i,
 						false, false) == InvalidOffsetNumber)
 			elog(PANIC, "_bt_restore_page: cannot add item to page");
-		from += itemsz;
-	}
 }
 
 static void
-- 
2.32.0.windows.1

