diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2012-08-20 19:51:42 +0300 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2012-08-20 19:58:21 +0300 |
commit | 51fed14d73ed3acd2282b531fb1396877e44e86a (patch) | |
tree | fa6fb3ca17d6a45dfd2bb088d049f510e6c51ef8 /src/backend/access/transam/xlog.c | |
parent | 9b2a237cee800ca11fb0415cc5484a46a8c1770d (diff) | |
download | postgresql-51fed14d73ed3acd2282b531fb1396877e44e86a.tar.gz postgresql-51fed14d73ed3acd2282b531fb1396877e44e86a.zip |
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.
Diffstat (limited to 'src/backend/access/transam/xlog.c')
-rw-r--r-- | src/backend/access/transam/xlog.c | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index efda4634d56..f24e2fb63e9 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -3707,8 +3707,17 @@ RecordIsValid(XLogRecord *record, XLogRecPtr recptr, int emode) uint32 len = record->xl_len; BkpBlock bkpb; char *blk; + char *recend = (char *) record + record->xl_tot_len; /* 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); 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))) 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)); + if (bkpb.hole_offset + bkpb.hole_length > BLCKSZ) { ereport(emode_for_corrupt_record(emode, recptr), @@ -3730,6 +3747,14 @@ RecordIsValid(XLogRecord *record, XLogRecPtr recptr, int emode) return false; } 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); blk += blen; } @@ -3879,8 +3904,8 @@ retry: /* * If the whole record header is on this page, validate it immediately. - * Otherwise validate it after reading the rest of the header from next - * page. + * Otherwise only do a basic sanity check on xl_tot_len, and validate the + * rest of the header after reading it from the next page. */ if (targetRecOff <= XLOG_BLCKSZ - SizeOfXLogRecord) { @@ -3889,7 +3914,16 @@ retry: gotheader = true; } 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; + } /* * Allocate or enlarge readRecordBuf as needed. To avoid useless small |