| From: | Henson Choi <assam258(at)gmail(dot)com> |
|---|---|
| To: | Tatsuo Ishii <ishii(at)postgresql(dot)org>, jian(dot)universality(at)gmail(dot)com |
| Cc: | zsolt(dot)parragi(at)percona(dot)com, sjjang112233(at)gmail(dot)com, vik(at)postgresfriends(dot)org, er(at)xs4all(dot)nl, jacob(dot)champion(at)enterprisedb(dot)com, david(dot)g(dot)johnston(at)gmail(dot)com, peter(at)eisentraut(dot)org, li(dot)evan(dot)chao(at)gmail(dot)com, pgsql-hackers(at)postgresql(dot)org, 신성준 <shinsj4653(at)gmail(dot)com> |
| Subject: | Re: Row pattern recognition |
| Date: | 2026-09-04 04:42:15 |
| Message-ID: | CAAAe_zAHbPO_jxVzZ5b=iyussKqi_wzJp1C-ubWA1LT7n++BZg@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi Tatsuo, Jian,
I would like to disagree with one part of this, late and after it has
been agreed to. Sorry for coming back to it only now.
>> I also intend to add below at the beginning of nfa_state_free
>> ``````
>> /* state at the free-list head was freed by the previous call */
>> if (unlikely(winstate->nfaStateFree == state))
>> elog(ERROR, "double free of RPR NFA state");
>> ``````
>
> Looks good to me.
I do not think this guard catches what its name claims. It detects
one thing only: the same pointer freed twice in a row, with no other
free in between. The comment says so itself -- it compares against
the state the previous call pushed. One unrelated free between the
two and it passes:
nfa_state_free(A) head = A
nfa_state_free(B) head = B, B->next = A
nfa_state_free(A) head is B, not A -- the check passes
A->next = B, head = A
The free list is now A -> B -> A and nothing reported it. Two
consecutive frees of one pointer is also the shape a reader is most
likely to catch in review, so the guard covers the case we would find
anyway and lets through the case that survives review.
> Ok. You are not confident the errors never happen. If so, using
> elog(ERROR) makes sense.
There were paths that failed to free, so the doubt is not an abstract
one. That was in February and March, and they were fixed. A great
deal has changed since, and I agree the memory handling is worth
verifying again -- I would rather that verification be done under
Valgrind than by a check compiled into release builds.
> On the hand if we are confident that the error is truley unlikely
> happen, using elog(ERROR) is just a waste of CPU.
Agreed, and it is worth saying where this one would land.
nfa_state_free() is called from eight sites, five of them inside the
per-row loops of nfa_match() and the advance paths, so whatever we put
in it is paid per state, per row, per partition.
And a double free is in fact already detected here, by a check that
does not have the limitation above.
In an ordinary build nothing is freed at all: nfa_state_free() pushes
the pointer onto a private list and the chunk stays live in
partcontext, so there is nothing for anything to observe. That is why
the function already carries:
#ifdef USE_VALGRIND
/* real free so Valgrind catches use-after-free instead of
recycling */
pfree(state);
#else
state->next = winstate->nfaStateFree;
winstate->nfaStateFree = state;
#endif
Under USE_VALGRIND that branch makes every free a real pfree(), and
since nothing ever assigns nfaStateFree there it stays NULL, so every
nfa_state_make() allocates afresh from MemoryContextAlloc(). Every
state therefore travels the ordinary palloc/pfree path.
USE_VALGRIND also defines MEMORY_CONTEXT_CHECKING on its own
(pg_config_manual.h), which turns on AllocSetFree()'s own guard:
/* Test for previously-freed chunk */
if (unlikely(chunk->requested_size == InvalidAllocSize))
elog(ERROR, "detected double pfree in %s %p",
set->header.name, chunk);
That is the check being proposed, already in the tree and without the
consecutive-free limitation: it tests a mark on the chunk itself
rather than the position of a list head, so it fires on any second
free. The comment above it names the same hazard -- "the result would
be a corrupted freelist that allows this chunk to get re-allocated
twice." Valgrind then adds the other half, since the freed chunk is
made NOACCESS: a state recycled while a caller still held it is
reported as an invalid read or write.
To be definite about it: this class of problem has to be verified by
other means than the one proposed -- in a Valgrind run, where the
detection already exists and is general, rather than by a branch
compiled into release builds.
Best regards,
Henson
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Paul A Jungwirth | 2026-09-04 04:43:39 | EXPLAIN ... FOR PORTION OF should not evaluate bounds |
| Previous Message | Ewan Young | 2026-09-04 04:39:59 | Re: FOR PORTION OF silently ignored on views with DO INSTEAD rules |