* with. (That is enough for all "normal" records, but very large commit or
* abort records might need more space.)
*
+ * The caller must make sure that "reclength" is valid and within the
+ * XLogRecordMaxSize limit.
+ *
* Note: This routine should *never* be called for xl_tot_len until the header
* of the record has been fully validated.
*/
static void
allocate_recordbuf(XLogReaderState *state, uint32 reclength)
{
- uint32 newSize = reclength;
+ uint32 newSize;
+
+ Assert(reclength <= XLogRecordMaxSize);
- newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
+ newSize = TYPEALIGN(XLOG_BLCKSZ, reclength);
newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ));
if (state->readRecordBuf)
(uint32) SizeOfXLogRecord, total_len);
goto err;
}
+
+ /*
+ * If the record length exceeds the maximum allowed size, don't try to
+ * reconstruct it. The backend enforces the same limit in
+ * XLogRecordAssemble().
+ */
+ 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;
}
(uint32) SizeOfXLogRecord, record->xl_tot_len);
return false;
}
+
+ 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,
$log_size),
"xl_tot_len short");
+# xl_tot_len is > XLogRecordMaxSize (presumably recycled garbage).
+$node->emit_wal(0);
+$end_lsn = $node->advance_wal_out_of_record_splitting_zone($WAL_BLOCK_SIZE);
+$node->stop('immediate');
+$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
+ build_record_header(1024 * 1024 * 1024));
+$log_size = -s $node->logfile;
+$node->start;
+ok( $node->log_contains(
+ "invalid record length at .*: expected at most 1069547520, got 1073741824",
+ $log_size),
+ "xl_tot_len short");
+
# xl_tot_len in final position, not big enough to span into a new page but
# also not eligible for regular record header validation
$node->emit_wal(0);
$end_lsn = $node->advance_wal_out_of_record_splitting_zone($WAL_BLOCK_SIZE);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+ build_record_header(2 * 1024 * 1024, 0, 0xdeadbeef));
$log_size = -s $node->logfile;
$node->start;
ok( $node->log_contains(
$end_lsn = $node->emit_wal(0);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn));
+ build_record_header(2 * 1024 * 1024, 0, $prev_lsn));
$log_size = -s $node->logfile;
$node->start;
ok($node->log_contains("invalid magic number 0000 .* LSN .*", $log_size),
$end_lsn = $node->emit_wal(0);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn));
+ build_record_header(2 * 1024 * 1024, 0, $prev_lsn));
$node->write_wal($TLI, start_of_next_page($end_lsn),
$WAL_SEGMENT_SIZE, build_page_header(0xcafe, 0, 1, 0));
$log_size = -s $node->logfile;
$end_lsn = $node->emit_wal(0);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn));
+ build_record_header(2 * 1024 * 1024, 0, $prev_lsn));
$node->write_wal($TLI, start_of_next_page($end_lsn),
$WAL_SEGMENT_SIZE, build_page_header($XLP_PAGE_MAGIC, 0, 1, 0xbaaaaaad));
$log_size = -s $node->logfile;
$end_lsn = $node->emit_wal(0);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn));
+ build_record_header(2 * 1024 * 1024, 42, $prev_lsn));
$node->write_wal(
$TLI,
start_of_next_page($end_lsn),
$end_lsn = $node->emit_wal(0);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn));
+ build_record_header(2 * 1024 * 1024, 42, $prev_lsn));
$node->write_wal($TLI, start_of_next_page($end_lsn),
$WAL_SEGMENT_SIZE,
build_page_header($XLP_PAGE_MAGIC, 0, 1, start_of_next_page($end_lsn)));
$end_lsn = $node->emit_wal(0);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn));
+ build_record_header(2 * 1024 * 1024, 42, $prev_lsn));
$node->write_wal(
$TLI,
start_of_next_page($end_lsn),
$end_lsn = $node->advance_wal_to_record_splitting_zone($WAL_BLOCK_SIZE);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+ build_record_header(2 * 1024 * 1024, 0, 0xdeadbeef));
$log_size = -s $node->logfile;
$node->start;
ok($node->log_contains("invalid magic number 0000 .* LSN .*", $log_size),
$end_lsn = $node->advance_wal_to_record_splitting_zone($WAL_BLOCK_SIZE);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+ build_record_header(2 * 1024 * 1024, 0, 0xdeadbeef));
$node->write_wal(
$TLI,
start_of_next_page($end_lsn),
$end_lsn = $node->advance_wal_to_record_splitting_zone($WAL_BLOCK_SIZE);
$node->stop('immediate');
$node->write_wal($TLI, $end_lsn, $WAL_SEGMENT_SIZE,
- build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+ build_record_header(2 * 1024 * 1024, 0, 0xdeadbeef));
$node->write_wal(
$TLI,
start_of_next_page($end_lsn),