From 2fd1333e0d83b4b7c07dd6b4eff3f7694cd4f32a Mon Sep 17 00:00:00 2001 From: David Karapetyan Date: Fri, 31 Jul 2026 11:17:34 +0200 Subject: [PATCH v3] Fix XLogReader mishandling of oversized multi-page records. XLogRecordAssemble() refuses records larger than XLogRecordMaxSize, but the reader only checked a minimum xl_tot_len. A crafted multi-page record with xl_tot_len near UINT32_MAX could pass contrecord length checks, overflow allocate_recordbuf()'s size math, and corrupt memory during reassembly (or hit related asserts under cassert). This patch avoids allocating memory for split-header records if the size of the record is definitely too large, avoiding "allocation too large" and size overflow -related issues. Co-authored-by: Matthias van de Meent --- src/backend/access/transam/xlogreader.c | 37 +++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c index 946907a2507..1226855e2fe 100644 --- a/src/backend/access/transam/xlogreader.c +++ b/src/backend/access/transam/xlogreader.c @@ -191,9 +191,11 @@ XLogReaderFree(XLogReaderState *state) static void allocate_recordbuf(XLogReaderState *state, uint32 reclength) { - uint32 newSize = reclength; + uint32 newSize; - newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ); + Assert(reclength <= INT32_MAX - BLCKSZ); + + newSize = TYPEALIGN(XLOG_BLCKSZ, reclength); newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ)); if (state->readRecordBuf) @@ -673,6 +675,21 @@ restart: (uint32) SizeOfXLogRecord, total_len); goto err; } + + /* + * If it's too large we shouldn't try to reconstruct the corrupted + * record; it could cause all kinds of issues through overflow, + * oversized allocations, and OOMs. + */ + if (total_len > XLogRecordMaxSize) + { + report_invalid_record(state, + "invalid record length at %X/%08X: expected at most %u, got %u", + LSN_FORMAT_ARGS(RecPtr), + XLogRecordMaxSize, total_len); + goto err; + } + /* We'll validate the header once we have the next page. */ gotheader = false; } @@ -1148,6 +1165,22 @@ ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr, (uint32) SizeOfXLogRecord, record->xl_tot_len); return false; } + + /* + * Symmetric with XLogRecordAssemble(): the reader must not attempt to + * reassemble or decode a record larger than XLogRecordMaxSize. Without + * this bound, a crafted multi-page xl_tot_len near UINT32_MAX can make + * allocate_recordbuf()'s size math overflow and corrupt memory during + * reassembly. + */ + if (record->xl_tot_len > XLogRecordMaxSize) + { + report_invalid_record(state, + "invalid record length at %X/%08X: expected at most %u, got %u", + LSN_FORMAT_ARGS(RecPtr), + XLogRecordMaxSize, record->xl_tot_len); + return false; + } if (!RmgrIdIsValid(record->xl_rmid)) { report_invalid_record(state, -- 2.50.1 (Apple Git-155)