Skip to content

Commit ffd1b6b

Browse files
committed
Add overflow protection for block-related data in WAL records
XLogRecordBlockHeader, the header holding the information for the data related to a block, tracks the length of the data appended to the WAL record with data_length (uint16). This limitation in size was not enforced by the public routine in charge of registering the data assembled later to form the WAL record inserted, XLogRegisterBufData(). Incorrectly used, it could lead to the generation of records with some of its data overflowed. This commit adds some safeguards to prevent that for the block data, complaining immediately if attempting to add to a record block information with a size larger than UINT16_MAX, which is the limit implied by the internal logic. Note that this also adjusts XLogRegisterData() and XLogRegisterBufData() so as the length of the WAL record data given by the caller is unsigned, matching with what gets stored in XLogRecData->len. Extracted from a larger patch by the same author. The original patch includes more protections when assembling a record in full that will be looked at separately later. Author: Matthias van de Meent Reviewed-by: Andres Freund, Heikki Linnakangas, Michael Paquier, David Zhang Discussion: https://postgr.es/m/CAEze2WgGiw+LZt+vHf8tWqB_6VxeLsMeoAuod0N=ij1q17n5pw@mail.gmail.com
1 parent 70988b7 commit ffd1b6b

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

src/backend/access/transam/xloginsert.c

+18-4
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ XLogRegisterBlock(uint8 block_id, RelFileLocator *rlocator, ForkNumber forknum,
348348
* XLogRecGetData().
349349
*/
350350
void
351-
XLogRegisterData(char *data, int len)
351+
XLogRegisterData(char *data, uint32 len)
352352
{
353353
XLogRecData *rdata;
354354

@@ -386,7 +386,7 @@ XLogRegisterData(char *data, int len)
386386
* limited)
387387
*/
388388
void
389-
XLogRegisterBufData(uint8 block_id, char *data, int len)
389+
XLogRegisterBufData(uint8 block_id, char *data, uint32 len)
390390
{
391391
registered_buffer *regbuf;
392392
XLogRecData *rdata;
@@ -399,8 +399,16 @@ XLogRegisterBufData(uint8 block_id, char *data, int len)
399399
elog(ERROR, "no block with id %d registered with WAL insertion",
400400
block_id);
401401

402-
if (num_rdatas >= max_rdatas)
402+
/*
403+
* Check against max_rdatas and ensure we do not register more data per
404+
* buffer than can be handled by the physical data format; i.e. that
405+
* regbuf->rdata_len does not grow beyond what
406+
* XLogRecordBlockHeader->data_length can hold.
407+
*/
408+
if (num_rdatas >= max_rdatas ||
409+
regbuf->rdata_len + len > UINT16_MAX)
403410
elog(ERROR, "too much WAL data");
411+
404412
rdata = &rdatas[num_rdatas++];
405413

406414
rdata->data = data;
@@ -756,12 +764,18 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
756764

757765
if (needs_data)
758766
{
767+
/*
768+
* When copying to XLogRecordBlockHeader, the length is narrowed
769+
* to an uint16. Double-check that it is still correct.
770+
*/
771+
Assert(regbuf->rdata_len <= UINT16_MAX);
772+
759773
/*
760774
* Link the caller-supplied rdata chain for this buffer to the
761775
* overall list.
762776
*/
763777
bkpb.fork_flags |= BKPBLOCK_HAS_DATA;
764-
bkpb.data_length = regbuf->rdata_len;
778+
bkpb.data_length = (uint16) regbuf->rdata_len;
765779
total_len += regbuf->rdata_len;
766780

767781
rdt_datas_last->next = regbuf->rdata_head;

src/include/access/xloginsert.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ extern void XLogBeginInsert(void);
4343
extern void XLogSetRecordFlags(uint8 flags);
4444
extern XLogRecPtr XLogInsert(RmgrId rmid, uint8 info);
4545
extern void XLogEnsureRecordSpace(int max_block_id, int ndatas);
46-
extern void XLogRegisterData(char *data, int len);
46+
extern void XLogRegisterData(char *data, uint32 len);
4747
extern void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags);
4848
extern void XLogRegisterBlock(uint8 block_id, RelFileLocator *rlocator,
4949
ForkNumber forknum, BlockNumber blknum, char *page,
5050
uint8 flags);
51-
extern void XLogRegisterBufData(uint8 block_id, char *data, int len);
51+
extern void XLogRegisterBufData(uint8 block_id, char *data, uint32 len);
5252
extern void XLogResetInsertion(void);
5353
extern bool XLogCheckBufferNeedsBackup(Buffer buffer);
5454

0 commit comments

Comments
 (0)