Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths - Mailing list pgsql-hackers

From Matthias van de Meent
Subject Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths
Date
Msg-id CAEze2WgEX-c6aPxMn2K_k5ZYYqfPN1qtxqa9COQM19PZbegV2w@mail.gmail.com
Whole thread Raw
In response to Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths  (Andres Freund <andres@anarazel.de>)
Responses Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths  (Matthias van de Meent <boekewurm+postgres@gmail.com>)
Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths  (Matthias van de Meent <boekewurm+postgres@gmail.com>)
List pgsql-hackers
On Mon, 14 Mar 2022 at 18:14, Andres Freund <andres@anarazel.de> wrote:
>
> Hi
>
> A random thought I had while thinking about the size limits: We could use the
> low bits of the length and xl_prev to store XLR_SPECIAL_REL_UPDATE |
> XLR_CHECK_CONSISTENCY and give rmgrs the full 8 bit of xl_info. Which would
> allow us to e.g. get away from needing Heap2. Which would aestethically be
> pleasing.

That would be interesting; though out of scope for this bug I'm trying to fix.

> On 2022-03-14 17:57:23 +0100, Matthias van de Meent wrote:
> > I'm not sure whether or not to include this in the test suite, though,
> > as this would require a machine with at least 1GB of memory available
> > for this test alone, and I don't know the current requirements for
> > running the test suite.
>
> We definitely shouldn't require this much RAM for the tests.
>
> It might be worth adding tests exercising edge cases around segment boundaries
> (and perhaps page boundaries) though. E.g. record headers split across pages
> and segments.
>
>
>
> > --- a/src/backend/access/transam/xloginsert.c
> > +++ b/src/backend/access/transam/xloginsert.c
> > @@ -338,10 +338,16 @@ XLogRegisterData(char *data, int len)
> >  {
> >       XLogRecData *rdata;
> >
> > -     Assert(begininsert_called);
> > +     Assert(begininsert_called && len >= 0 && AllocSizeIsValid(len));
>
> Shouldn't we just make the length argument unsigned?

I've applied that in the attached revision; but I'd like to note that
this makes the fix less straightforward to backpatch; as the changes
to the public function signatures shouldn't be applied in older
versions.

> > -     if (num_rdatas >= max_rdatas)
> > +     /*
> > +      * Check against max_rdatas; and ensure we don't fill a record with
> > +      * more data than can be replayed
> > +      */
> > +     if (unlikely(num_rdatas >= max_rdatas ||
> > +                              !AllocSizeIsValid((uint64) mainrdata_len + (uint64) len)))
> >               elog(ERROR, "too much WAL data");
> > +
> >       rdata = &rdatas[num_rdatas++];
>
> Personally I'd write it as unlikely(num_rdatas >= max_rdatas) || unlikely(...)
> but I doubt if it makes an actual difference to the compiler.

Agreed, updated.

> >       rdata->data = data;
> > @@ -377,7 +383,7 @@ XLogRegisterBufData(uint8 block_id, char *data, int len)
> >       registered_buffer *regbuf;
> >       XLogRecData *rdata;
> >
> > -     Assert(begininsert_called);
> > +     Assert(begininsert_called && len >= 0 && len <= UINT16_MAX);
> >
> >       /* find the registered buffer struct */
> >       regbuf = ®istered_buffers[block_id];
> > @@ -385,8 +391,14 @@ XLogRegisterBufData(uint8 block_id, char *data, int len)
> >               elog(ERROR, "no block with id %d registered with WAL insertion",
> >                        block_id);
> >
> > -     if (num_rdatas >= max_rdatas)
> > +     /*
> > +      * Check against max_rdatas; and ensure we don't register more data per
> > +      * buffer than can be handled by the physical record format.
> > +      */
> > +     if (unlikely(num_rdatas >= max_rdatas ||
> > +                              regbuf->rdata_len + len > UINT16_MAX))
> >               elog(ERROR, "too much WAL data");
> > +
> >       rdata = &rdatas[num_rdatas++];
>
> Given the repeated check it might be worth to just put it in a static inline
> used from the relevant places (which'd generate less code because the same
> line number would be used for all the checks).

The check itself is slightly different in those 3 places; but the
error message is shared. Do you mean to extract the elog() into a
static inline function (as attached), or did I misunderstand?


-Matthias

Attachment

pgsql-hackers by date:

Previous
From: "Daniel Verite"
Date:
Subject: Re: ICU for global collation
Next
From: Justin Pryzby
Date:
Subject: Re: pg14 psql broke \d datname.nspname.relname