Re: fill_seq_fork_with_data() initializes buffer without lock

Поиск
Список
Период
Сортировка
От Andres Freund
Тема Re: fill_seq_fork_with_data() initializes buffer without lock
Дата
Msg-id 20230404232338.ttae5jzqs3wm46ui@awork3.anarazel.de
обсуждение исходный текст
Ответ на fill_seq_fork_with_data() initializes buffer without lock  (Andres Freund <andres@anarazel.de>)
Список pgsql-hackers
Hi,

On 2023-04-04 11:55:01 -0700, Andres Freund wrote:
> Look at:
>
> static void
> fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
> {
>     Buffer        buf;
>     Page        page;
>     sequence_magic *sm;
>     OffsetNumber offnum;
>
>     /* Initialize first page of relation with special magic number */
>
>     buf = ReadBufferExtended(rel, forkNum, P_NEW, RBM_NORMAL, NULL);
>     Assert(BufferGetBlockNumber(buf) == 0);
>
>     page = BufferGetPage(buf);
>
>     PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic));
>     sm = (sequence_magic *) PageGetSpecialPointer(page);
>     sm->magic = SEQ_MAGIC;
>
>     /* Now insert sequence tuple */
>
>     LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
>
>
> Clearly we are modifying the page (via PageInit()), without holding a buffer
> lock, which is only acquired subsequently.
>
> It's clearly unlikely to cause bad consequences - the sequence doesn't yet
> really exist, and we haven't seen any reports of a problem - but it doesn't
> seem quite impossible that it would cause problems.
>
> As far as I can tell, this goes back to the initial addition of the sequence
> code, in e8647c45d66a - I'm too lazy to figure out whether it possibly wasn't
> a problem in 1997 for some reason.

Robert suggested to add an assertion to PageInit() to defend against such
omissions. I quickly hacked one together. The assertion immediately found the
issue here, but no other currently existing ones.

I'm planning to push a fix for this to HEAD. Given that the risk seems low and
the issue is so longstanding, it doesn't seem quite worth backpatching?


FWIW, the assertion I used is:

    if (page >= BufferBlocks && page <= BufferBlocks + BLCKSZ * NBuffers)
    {
        Buffer buffer = (page - BufferBlocks) / BLCKSZ + 1;
        BufferDesc *buf = GetBufferDescriptor(buffer - 1);

        Assert(LWLockHeldByMeInMode(BufferDescriptorGetContentLock(buf), LW_EXCLUSIVE));
    }

If there's interest in having such an assertion permenantly, it clearly can't
live in bufpage.c.

I have a bit of a hard time coming up with a good name. Any suggestions?

Greetings,

Andres Freund



В списке pgsql-hackers по дате отправления:

Предыдущее
От: Kirk Wolak
Дата:
Сообщение: Re: psql \watch 2nd argument: iteration count
Следующее
От: Andres Freund
Дата:
Сообщение: Re: monitoring usage count distribution