Don't get confused if a WAL partial record header has xl_tot_len == 0.
If a WAL record header was split across pages, but xl_tot_len was 0, we would get confused and conclude that we had already read the whole record, and proceed to CRC check it. That can lead to a crash in RecordIsValid(), which isn't careful to not read beyond end-of-record, as defined by xl_tot_len. Add an explicit sanity check for xl_tot_len <= SizeOfXlogRecord. Also, make RecordIsValid() more robust by checking in each step that it doesn't try to access memory beyond end of record, even if a length field in the record's or a backup block's header is bogus. Per report and analysis by Tom Lane.
This commit is contained in:
parent
9b2a237cee
commit
51fed14d73
|
@ -3707,8 +3707,17 @@ RecordIsValid(XLogRecord *record, XLogRecPtr recptr, int emode)
|
||||||
uint32 len = record->xl_len;
|
uint32 len = record->xl_len;
|
||||||
BkpBlock bkpb;
|
BkpBlock bkpb;
|
||||||
char *blk;
|
char *blk;
|
||||||
|
char *recend = (char *) record + record->xl_tot_len;
|
||||||
|
|
||||||
/* First the rmgr data */
|
/* First the rmgr data */
|
||||||
|
if (XLogRecGetData(record) + len > recend)
|
||||||
|
{
|
||||||
|
/* ValidXLogRecordHeader() should've caught this already... */
|
||||||
|
ereport(emode_for_corrupt_record(emode, recptr),
|
||||||
|
(errmsg("invalid record length at %X/%X",
|
||||||
|
(uint32) (recptr >> 32), (uint32) recptr)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
INIT_CRC32(crc);
|
INIT_CRC32(crc);
|
||||||
COMP_CRC32(crc, XLogRecGetData(record), len);
|
COMP_CRC32(crc, XLogRecGetData(record), len);
|
||||||
|
|
||||||
|
@ -3721,7 +3730,15 @@ RecordIsValid(XLogRecord *record, XLogRecPtr recptr, int emode)
|
||||||
if (!(record->xl_info & XLR_SET_BKP_BLOCK(i)))
|
if (!(record->xl_info & XLR_SET_BKP_BLOCK(i)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (blk + sizeof(BkpBlock) > recend)
|
||||||
|
{
|
||||||
|
ereport(emode_for_corrupt_record(emode, recptr),
|
||||||
|
(errmsg("incorrect backup block size in record at %X/%X",
|
||||||
|
(uint32) (recptr >> 32), (uint32) recptr)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
memcpy(&bkpb, blk, sizeof(BkpBlock));
|
memcpy(&bkpb, blk, sizeof(BkpBlock));
|
||||||
|
|
||||||
if (bkpb.hole_offset + bkpb.hole_length > BLCKSZ)
|
if (bkpb.hole_offset + bkpb.hole_length > BLCKSZ)
|
||||||
{
|
{
|
||||||
ereport(emode_for_corrupt_record(emode, recptr),
|
ereport(emode_for_corrupt_record(emode, recptr),
|
||||||
|
@ -3730,6 +3747,14 @@ RecordIsValid(XLogRecord *record, XLogRecPtr recptr, int emode)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
blen = sizeof(BkpBlock) + BLCKSZ - bkpb.hole_length;
|
blen = sizeof(BkpBlock) + BLCKSZ - bkpb.hole_length;
|
||||||
|
|
||||||
|
if (blk + blen > recend)
|
||||||
|
{
|
||||||
|
ereport(emode_for_corrupt_record(emode, recptr),
|
||||||
|
(errmsg("invalid backup block size in record at %X/%X",
|
||||||
|
(uint32) (recptr >> 32), (uint32) recptr)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
COMP_CRC32(crc, blk, blen);
|
COMP_CRC32(crc, blk, blen);
|
||||||
blk += blen;
|
blk += blen;
|
||||||
}
|
}
|
||||||
|
@ -3879,8 +3904,8 @@ retry:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the whole record header is on this page, validate it immediately.
|
* If the whole record header is on this page, validate it immediately.
|
||||||
* Otherwise validate it after reading the rest of the header from next
|
* Otherwise only do a basic sanity check on xl_tot_len, and validate the
|
||||||
* page.
|
* rest of the header after reading it from the next page.
|
||||||
*/
|
*/
|
||||||
if (targetRecOff <= XLOG_BLCKSZ - SizeOfXLogRecord)
|
if (targetRecOff <= XLOG_BLCKSZ - SizeOfXLogRecord)
|
||||||
{
|
{
|
||||||
|
@ -3889,7 +3914,16 @@ retry:
|
||||||
gotheader = true;
|
gotheader = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (total_len < SizeOfXLogRecord)
|
||||||
|
{
|
||||||
|
ereport(emode_for_corrupt_record(emode, *RecPtr),
|
||||||
|
(errmsg("invalid record length at %X/%X",
|
||||||
|
(uint32) ((*RecPtr) >> 32), (uint32) *RecPtr)));
|
||||||
|
goto next_record_is_invalid;
|
||||||
|
}
|
||||||
gotheader = false;
|
gotheader = false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate or enlarge readRecordBuf as needed. To avoid useless small
|
* Allocate or enlarge readRecordBuf as needed. To avoid useless small
|
||||||
|
|
Loading…
Reference in New Issue