Re: BUG #19480: PL/Python SRF crashes (SIGSEGV) when function is replaced mid-iteration: use-after-free in PLy_funct

Matheus Alcantara <matheusssilv97@gmail.com>

From: "Matheus Alcantara" <matheusssilv97@gmail.com>
To: "Tom Lane" <tgl@sss.pgh.pa.us>
Cc: <adoros@starfishstorage.com>, <pgsql-bugs@lists.postgresql.org>
Date: 2026-06-05T18:09:26Z
Lists: pgsql-bugs

Attachments

On Mon Jun 1, 2026 at 8:26 PM -03, Tom Lane wrote:
> Yeah, that was my suspicion as well.  funccache.c exists because
> I realized that SQL-language functions (executor/functions.c) were
> going to need logic that plpgsql had had for years.
>
> Actually ... if memory serves, SQL-language functions use ValuePerCall
> mode, so there probably already is a solution to this embedded in
> functions.c.  Did you look at that?
>

I dind't look at this before but this was exactly the right call. SQL
functions handle this by maintaining a per-call-site cache struct
(SQLFunctionCache) in fn_extra that holds both the pointer to the
long-lived hash entry and the execution state. The use_count is
incremented when we first obtain the function and decremented via a
MemoryContextCallback when fn_mcxt is deleted.

I've adapted the same approach for PL/Python. The main changes are:

PLyProcedure now embeds CachedFunction as its first member and is
managed by cached_function_compile(). A new PLyProcedureCache struct
lives in fn_extra and holds the pointer to PLyProcedure plus SRF state.
For cleanup, I use a MemoryContextCallback on fn_mcxt to decrement
use_count, and an ExprContextCallback to clean up Python iterator state
when the SRF is interrupted.

Since fn_extra is now used for PLyProcedureCache, I had to remove the
SRF macros and switch to direct isDone signaling via ReturnSetInfo,
which is how SQL functions do it anyway.

I also fixed the validator to create a fake fcinfo with the correct
fn_oid (the function being validated), matching what PL/pgSQL does.

Patch attached.

--
Matheus Alcantara
EDB: https://www.enterprisedb.com

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. plpython: Use funccache.c infrastructure for procedure caching.