Hi, forgot to exec pgindent for patch, so attach new version with indents fixes.

Best regards,

Maksim Melnikov
From d218fdcdfc01642a01f488c6a01f55f10668f399 Mon Sep 17 00:00:00 2001
From: Maksim Melnikov <m.melni...@postgrespro.ru>
Date: Thu, 14 Aug 2025 11:11:30 +0300
Subject: [PATCH v2] Pre-check potential XLogRecord oversize.

XLogRecord size check is placed in critical section and in case
of failure PANIC will be generated. It seems not good, so to
avoid this, we can calculate approximate xlog record size before
critical section and check it.

Size prediction is based on xlog update and xlog delete logic and can
be revised in case of it changing, now buf size is limited by
UINT16_MAX(Assert(regbuf->rdata_len <= UINT16_MAX) in xloginsert).

Anyway to accommodate some overhead, 1M is substract from predicted
value. It seems now it is quite enough.
---
 src/backend/access/heap/heapam.c        | 30 +++++++++++++++++++++++++
 src/backend/access/transam/xloginsert.c | 20 +++++++++++++++++
 src/include/access/xloginsert.h         |  1 +
 3 files changed, 51 insertions(+)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 0dcd6ee817e..7c024174f0d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -61,6 +61,7 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
 								  Buffer newbuf, HeapTuple oldtup,
 								  HeapTuple newtup, HeapTuple old_key_tuple,
 								  bool all_visible_cleared, bool new_all_visible_cleared);
+static void log_heap_precheck(Relation reln, HeapTuple tp);
 #ifdef USE_ASSERT_CHECKING
 static void check_lock_if_inplace_updateable_rel(Relation relation,
 												 ItemPointer otid,
@@ -9043,6 +9044,33 @@ log_heap_update(Relation reln, Buffer oldbuf,
 	return recptr;
 }
 
+/*
+ * Pre-check potential XLogRecord oversize. XLogRecord will be created
+ * later, and it size will be checked, but it will occur in critical
+ * section and in case of failure core dump will be generated.
+ * It seems not good, so to avoid this, we can calculate approximate
+ * xlog record size here and check it.
+ *
+ * Size prediction is based on xlog update and xlog delete logic and can
+ * be revised in case of it changing, now buf size is limited by
+ * UINT16_MAX(Assert(regbuf->rdata_len <= UINT16_MAX) in xloginsert).
+ *
+ * Anyway to accommodate some overhead, 1M is substract from predicted
+ * value. It seems now it is quite enough.
+ */
+static void
+log_heap_precheck(Relation reln, HeapTuple tp)
+{
+#define XLogRecordMaxOverhead ((uint32) (1024 * 1024))
+
+	if (tp && RelationIsLogicallyLogged(reln))
+	{
+		uint32		data_len = tp->t_len - SizeofHeapTupleHeader;
+
+		XLogPreCheckSize(data_len + XLogRecordMaxOverhead);
+	}
+}
+
 /*
  * Perform XLogInsert of an XLOG_HEAP2_NEW_CID record
  *
@@ -9160,6 +9188,7 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_required,
 			*copy = true;
 			tp = toast_flatten_tuple(tp, desc);
 		}
+		log_heap_precheck(relation, tp);
 		return tp;
 	}
 
@@ -9216,6 +9245,7 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_required,
 		heap_freetuple(oldtup);
 	}
 
+	log_heap_precheck(relation, key_tuple);
 	return key_tuple;
 }
 
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index c7571429e8e..8b96d3d21d3 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -1052,6 +1052,26 @@ XLogCheckBufferNeedsBackup(Buffer buffer)
 	return false;				/* buffer does not need to be backed up */
 }
 
+/*
+ * Ensure that the XLogRecord is not too large.
+ *
+ * XLogReader machinery is only able to handle records up to a certain
+ * size (ignoring machine resource limitations), so make sure that we will
+ * not emit records larger than the sizes advertised to be supported.
+ */
+void
+XLogPreCheckSize(uint32 data_len)
+{
+	if (data_len > XLogRecordMaxSize)
+	{
+		ereport(ERROR,
+				(errmsg_internal("oversized WAL record"),
+				 errdetail_internal("WAL record would be %" PRIu32
+									" bytes (of maximum %u bytes).",
+									data_len, XLogRecordMaxSize)));
+	}
+}
+
 /*
  * Write a backup block if needed when we are setting a hint. Note that
  * this may be called for a variety of page types, not just heaps.
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index d6a71415d4f..7b40a824d9b 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -54,6 +54,7 @@ extern void XLogRegisterBlock(uint8 block_id, RelFileLocator *rlocator,
 extern void XLogRegisterBufData(uint8 block_id, const void *data, uint32 len);
 extern void XLogResetInsertion(void);
 extern bool XLogCheckBufferNeedsBackup(Buffer buffer);
+extern void XLogPreCheckSize(uint32 size);
 
 extern XLogRecPtr log_newpage(RelFileLocator *rlocator, ForkNumber forknum,
 							  BlockNumber blkno, Page page, bool page_std);
-- 
2.43.0

Reply via email to