From 2f2a37c62f74779bbe5d988040922cf7a21eabc9 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 7 Sep 2025 12:06:03 +0300 Subject: [PATCH 1/2] Perform check for oversized WAL record before calculating record CRC --- src/backend/access/transam/xloginsert.c | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c index c7571429e8e9..e8c55924c125 100644 --- a/src/backend/access/transam/xloginsert.c +++ b/src/backend/access/transam/xloginsert.c @@ -904,19 +904,6 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, hdr_rdt.len = (scratch - hdr_scratch); total_len += hdr_rdt.len; - /* - * Calculate CRC of the data - * - * Note that the record header isn't added into the CRC initially since we - * don't know the prev-link yet. Thus, the CRC will represent the CRC of - * the whole record in the order: rdata, then backup blocks, then record - * header. - */ - INIT_CRC32C(rdata_crc); - COMP_CRC32C(rdata_crc, hdr_scratch + SizeOfXLogRecord, hdr_rdt.len - SizeOfXLogRecord); - for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next) - COMP_CRC32C(rdata_crc, rdt->data, rdt->len); - /* * Ensure that the XLogRecord is not too large. * @@ -930,6 +917,19 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, errdetail_internal("WAL record would be %" PRIu64 " bytes (of maximum %u bytes); rmid %u flags %u.", total_len, XLogRecordMaxSize, rmid, info))); + /* + * Calculate CRC of the data + * + * Note that the record header isn't added into the CRC initially since we + * don't know the prev-link yet. Thus, the CRC will represent the CRC of + * the whole record in the order: rdata, then backup blocks, then record + * header. + */ + INIT_CRC32C(rdata_crc); + COMP_CRC32C(rdata_crc, hdr_scratch + SizeOfXLogRecord, hdr_rdt.len - SizeOfXLogRecord); + for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next) + COMP_CRC32C(rdata_crc, rdt->data, rdt->len); + /* * Fill in the fields in the record header. Prev-link is filled in later, * once we know where in the WAL the record will be inserted. The CRC does From 9dbcd1c281c2f623b7033ba0330bbdeae5144f74 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 7 Sep 2025 12:06:27 +0300 Subject: [PATCH 2/2] Add a TAP test for oversized WAL records --- .../recovery/t/049_oversized_wal_record.pl | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/test/recovery/t/049_oversized_wal_record.pl diff --git a/src/test/recovery/t/049_oversized_wal_record.pl b/src/test/recovery/t/049_oversized_wal_record.pl new file mode 100644 index 000000000000..8604ef53d48b --- /dev/null +++ b/src/test/recovery/t/049_oversized_wal_record.pl @@ -0,0 +1,50 @@ + +# Copyright (c) 2025, PostgreSQL Global Development Group + +# Try to create a WAL record which is larger than the limit XLogRecordMaxSize. +# That should raise an error. + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use Test::More; + +# Disable the test unless requested explicitly because it requires 1Gb of memory +if (exists $ENV{PG_TEST_EXTRA} + && $ENV{PG_TEST_EXTRA} =~ m/\boversized_wal_record\b/) +{ + plan tests => 4; +} +else +{ + plan skip_all => + 'Skipping oversized_wal_record test as it requires a lot of memory'; +} + +note "Check handing of oversized WAL records"; + +# Initialize and start basic node +my $node = PostgreSQL::Test::Cluster->new('node'); + +$node->init; + +$node->start; + +# Insert oversized record +my ($ret, $stdout, $stderr) = $node->psql( + 'postgres', + "select pg_logical_emit_message(false, 'a', repeat('00000000', (1020 * 1024 * 1024)/8+1), true)", + on_error_die => 0 + ); + +# Verify we have error +ok($ret != 0, 'exit code is non-zero'); + +ok($stderr =~ qr/ERROR: oversized WAL record/, 'expect error'); + +ok($stderr =~ qr/DETAIL: WAL record would be [0-9]+ bytes \(of maximum [0-9]+ bytes\);/, + 'error details'); + +$node->stop; + +done_testing();