public inbox for [email protected]
help / color / mirror / Atom feedFrom: Emond Papegaaij <[email protected]>
To: [email protected]
Subject: Use-after-free crash
Date: Thu, 11 Jun 2026 09:59:38 +0200
Message-ID: <CAGXsc+aFfv2_PUrEdCt8TousYP9dPmty3i+0mkJpWwVg=fVN+Q@mail.gmail.com> (raw)
Hi,
Yesterday one of our tests detected a segmentation fault in pgpool,
caused by a use-after-free. It is triggered during the shutdown of the
process, so it probably isn't too harmful, but still I think it should
be fixed. I've worked with Claude Code to build a patch (against 4.7)
and a reproducer of the problem. The patch also contains a detailed
explanation of how this crashes in the commit message. To run the
reproducer:
tar xzf pgpool-uaf-repro.tar.gz
cd pgpool-uaf-repro
PGPOOL=/path/to/their/pgpool ./reproduce.sh
Best regards,
Emond
Attachments:
[text/x-patch] 0001-Fix-use-after-free-of-query-context-after-a-backend-.patch (4.4K, ../CAGXsc+aFfv2_PUrEdCt8TousYP9dPmty3i+0mkJpWwVg=fVN+Q@mail.gmail.com/2-0001-Fix-use-after-free-of-query-context-after-a-backend-.patch)
download | inline diff:
From 9b4f7d5f4f3a8ebadcabda6f1993790441a75b80 Mon Sep 17 00:00:00 2001
From: Emond Papegaaij <[email protected]>
Date: Thu, 11 Jun 2026 09:37:44 +0200
Subject: [PATCH] Fix use-after-free of query context after a backend node
shutdown
A POOL_QUERY_CONTEXT is referenced from the session scoped sent message
list (session_context->message_list) and pending message list, which
live in session_context->memory_context and survive for the whole
client session. However pool_init_query_context() allocated the query
context in the global QueryContext memory context.
do_child() resets QueryContext (MemoryContextResetAndDeleteChildren)
at the top of every iteration of its query processing loop. In normal
operation pool_process_query() runs the whole session in a single
iteration, so QueryContext is effectively session lived and this is
harmless. But when pool_process_query() returns POOL_CONTINUE in the
middle of a session, the loop iterates and QueryContext is reset while
the sent/pending message lists still reference query contexts that were
allocated in it. The freed query contexts are later dereferenced (for
example from pool_clear_sent_message_list() in reset_connection(), or
from pool_remove_sent_message() while binding a reused statement name),
causing a use-after-free and a SIGSEGV.
This is reachable when a backend node is shut down by an administrative
command (e.g. "fast" shutdown of PostgreSQL) while a client session that
has named prepared statements is open. read_packets_and_process()
detects the admin shutdown, sets was_error and returns POOL_CONTINUE
(cont = false), so pool_process_query() returns to do_child() mid
session and the next loop iteration frees the still referenced query
contexts.
Fix this by allocating the query context under the session memory
context rather than the per-iteration QueryContext, so its lifetime
matches the session scoped lists that reference it. When there is no
session context (internal queries issued before a session exists) fall
back to QueryContext, preserving the previous behaviour. Query contexts
are still freed explicitly by pool_query_context_destroy(), and at the
latest when the session memory context is destroyed, so this does not
change memory reclamation for the normal single-iteration case (in which
QueryContext was likewise only freed at session end).
---
src/context/pool_query_context.c | 34 +++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/src/context/pool_query_context.c b/src/context/pool_query_context.c
index a056ac596..7eef39367 100644
--- a/src/context/pool_query_context.c
+++ b/src/context/pool_query_context.c
@@ -78,15 +78,35 @@ static char *get_associated_object_from_dml_adaptive_relations
POOL_QUERY_CONTEXT *
pool_init_query_context(void)
{
- MemoryContext memory_context = AllocSetContextCreate(QueryContext,
- "QueryContextMemoryContext",
- ALLOCSET_SMALL_MINSIZE,
- ALLOCSET_SMALL_INITSIZE,
- ALLOCSET_SMALL_MAXSIZE);
-
- MemoryContext oldcontext = MemoryContextSwitchTo(memory_context);
+ POOL_SESSION_CONTEXT *session_context;
+ MemoryContext parent;
+ MemoryContext memory_context;
+ MemoryContext oldcontext;
POOL_QUERY_CONTEXT *qc;
+ /*
+ * Parent the query context under the session memory context rather than
+ * the per-iteration QueryContext. A query context is referenced from the
+ * session scoped sent message list and pending message list. These lists
+ * outlive the QueryContext, which do_child() resets between the iterations
+ * of its query processing loop (for instance when pool_process_query()
+ * returns after a backend node was shut down). If the query context lived
+ * in QueryContext, such a reset would free it while the message lists
+ * still reference it, resulting in a use-after-free. When there is no
+ * session context (e.g. internal queries issued before a session exists),
+ * fall back to QueryContext.
+ */
+ session_context = pool_get_session_context(true);
+ parent = session_context ? session_context->memory_context : QueryContext;
+
+ memory_context = AllocSetContextCreate(parent,
+ "QueryContextMemoryContext",
+ ALLOCSET_SMALL_MINSIZE,
+ ALLOCSET_SMALL_INITSIZE,
+ ALLOCSET_SMALL_MAXSIZE);
+
+ oldcontext = MemoryContextSwitchTo(memory_context);
+
qc = palloc0(sizeof(*qc));
qc->memory_context = memory_context;
MemoryContextSwitchTo(oldcontext);
--
2.53.0
[application/gzip] pgpool-uaf-repro.tar.gz (9.6K, ../CAGXsc+aFfv2_PUrEdCt8TousYP9dPmty3i+0mkJpWwVg=fVN+Q@mail.gmail.com/3-pgpool-uaf-repro.tar.gz)
download
view thread (5+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: Use-after-free crash
In-Reply-To: <CAGXsc+aFfv2_PUrEdCt8TousYP9dPmty3i+0mkJpWwVg=fVN+Q@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox