| From: | David K <dkarapetyan(at)gmail(dot)com> |
|---|---|
| To: | Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com> |
| Cc: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | Re: Bug: XLogReader mishandles oversized multi-page xl_tot_len (potential memory corruption) |
| Date: | 2026-07-30 20:36:00 |
| Message-ID: | CALmTjZCKamz8pr8E7tWtMz19PE0VehaRvKf1Mq4x9XrR1gVrkQ@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Thanks Matthias for the review.
Patch is attached with the following changes:
- keep XLogRecordMaxSize checks + Assert on reclength
- use add_size/mul_size instead of a bare size_t cast
- TYPEALIGN-equivalent roundup without an extra page when already aligned
- comment on the new oversized-length check
On Mon, Jul 27, 2026 at 4:35 AM Matthias van de Meent <
boekewurm+postgres(at)gmail(dot)com> wrote:
> On Mon, 27 Jul 2026 at 12:20, David K <dkarapetyan(at)gmail(dot)com> wrote:
> >
> > 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.
>
> Yep.
>
> > 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.
>
> This is not exactly corect. The distinction between size_t and uint32
> is nothing more than cosmetic on 32-bit systems, so just changing
> between the types won't change a thing there. You'll have to use the
> add/mul_size helpers (palloc.h) if you want to be certain unintended
> overflows are detected across all platforms.
>
> ---
>
> patch:
> I only reviewed the xlogreader changes:
>
> > +++ b/src/backend/access/transam/xlogreader.c
>
>
> > * Note: This routine should *never* be called for xl_tot_len until the
> header
> > - * of the record has been fully validated.
> > + * of the record has been fully validated (including the
> XLogRecordMaxSize
> > + * bound). Size math uses size_t so near-UINT32_MAX lengths cannot
> wrap to a
> > + * small allocation.
>
> The reclength parameter should have a value that cannot overflow with
> the calculations we're doing here; that's what the new checks of the
> patch prevent. An Assert() to this effect should be sufficient; the
> change to size_t is therefore not necessary.
>
> Additionally, we can avoid the additional XLOG_BLCKSZ bytes of memory
> usage when the record size is a multiple of XLOG_BLCKSZ by using
> correctly type-aligned lengths, like so:
>
> - newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
> + newSize = TYPEALIGN(XLOG_BLCKSZ, newSize);
>
> > - /* There may be no next page if it's too small. */
> > + /*
> > + * There may be no next page if it's too small. Cap xl_tot_len
> before
> > + * contrecord reassembly so we never allocate or copy based on a
> > + * garbage length from a recycled page.
> > + */
>
> Please put the new comment content on the newly added if-statement
> that actually does the record-is-oversized check.
>
>
> Kind regards,
>
> Matthias van de Meent
> Databricks (https://www.databricks.com)
>
| Attachment | Content-Type | Size |
|---|---|---|
| 0001-v2-Fix-XLogReader-mishandling-of-oversized-multi-page-records.patch | application/octet-stream | 17.7 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Masahiko Sawada | 2026-07-30 20:58:42 | Re: Fix "unexpected logical decoding status change" error; from concurrent logical decoding activation |
| Previous Message | Justin Pryzby | 2026-07-30 20:29:54 | Re: pg19b1: stuck in LockBuffer |