Hi,

An automated AI review of the WAL reader found that XLogReader does not
enforce XLogRecordMaxSize on xl_tot_len. The insert path has a check:
  XLogRecordAssemble() rejects total_len > XLogRecordMaxSize
but the reader only checks a minimum length. That asymmetry allows a
crafted or corrupted multi-page record reassembly to overflow.

More details
---
A multi-page record with:
  - xl_tot_len near UINT32_MAX (e.g. 0xFFFFF000), far above
XLogRecordMaxSize (1020 MB)
  - consistent XLP_FIRST_IS_CONTRECORD / xlp_rem_len on continuation pages
is not rejected early. allocate_recordbuf() then rounds the length with
uint32 arithmetic:
  newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
For lengths near UINT32_MAX this wraps around to a small buffer (~40 kB).
Later reassembly steps either:
  - under USE_ASSERT_CHECKING: abort on
      Assert(gotlen <= lengthof(save_copy))
    in XLogDecodeNextRecord (save_copy is only 2 * XLOG_BLCKSZ), or
  - without asserts: stack/heap overflow while copying contrecord data
    (overflowing the 2-page save_copy on the stack)
CRC validation happens after reassembly has allocated and copied based on
xl_tot_len, so a valid CRC is not required (only a plausible header and
matching contrecord lengths).

Impact
---
This does not affect replay of valid WAL produced by a healthy primary. It
affects consumers of untrusted or corrupt multi-page WAL, including:
  - crash recovery / archive recovery if such WAL is present
  - physical standbys replaying bad segments
  - tools such as pg_waldump that use the same frontend XLogReader

Fix
---
1. Reject xl_tot_len > XLogRecordMaxSize in ValidXLogRecordHeader(), and on
the partial-header path before multi-page reassembly starts (symmetric with
XLogRecordAssemble()).
2. Compute reassembly buffer sizes with size_t in allocate_recordbuf() so
near-UINT32_MAX lengths cannot wrap even if a caller forgets the bound.

With the fix, the reader returns an error, e.g.:
  invalid record length at 0/00000028: expected at most 1069547520, got
4294963200

Test
---
A small frontend module exercises the reader with in-memory crafted pages:
  src/test/modules/test_xlogreader/
  make -C src/test/modules/test_xlogreader
  ./src/test/modules/test_xlogreader/test_xlogreader_oversized
Before the fix: abort (cassert) or crash.
After the fix: exits 0 and prints the rejection message above.

Patch
---
Patch against current master is attached
(0001-Fix-XLogReader-mishandling-of-oversized-multi-page-r.patch).

Thanks,
David Karapetyan
[email protected]

Attachment: 0001-Fix-XLogReader-mishandling-of-oversized-multi-page-r.patch
Description: Binary data

Reply via email to