Skip to content

Commit e557c82

Browse files
committed
Avoid memory leak in validation of a PL/Python trigger function.
If we're trying to perform check_function_bodies validation of a PL/Python trigger function, we create a new PLyProcedure, but we don't put it into the PLy_procedure_cache hash table. (Doing so would be useless, since we don't have the relation OID that is part of the cache key for a trigger function, so we could not make an entry that would be found by later uses.) However, we didn't think through what to do instead, with the result that the PLyProcedure was simply leaked. It would take a pretty large number of CREATE FUNCTION operations for this to amount to a serious problem, but it's easy to see the memory bloat if you do CREATE OR REPLACE FUNCTION in a loop. To fix, have PLy_procedure_get delete the new PLyProcedure and return NULL if it's not going to cache the PLyProcedure. I considered making plpython3_validator do the cleanup instead, which would be more natural. But then plpython3_validator would have to know the rules under which PLy_procedure_get returns a non-cached PLyProcedure, else it risks deleting something that's pointed to by a cache entry. On the whole it seems more robust to deal with the case inside PLy_procedure_get. Found by the new version of Coverity (nice catch!). In the end I feel this fix is more about satisfying Coverity than about fixing a real-world problem, so I'm not going to back-patch.
1 parent 9f9a043 commit e557c82

File tree

2 files changed

+13
-2
lines changed

2 files changed

+13
-2
lines changed

src/pl/plpython/plpy_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ plpython3_validator(PG_FUNCTION_ARGS)
133133
ReleaseSysCache(tuple);
134134

135135
/* We can't validate triggers against any particular table ... */
136-
PLy_procedure_get(funcoid, InvalidOid, is_trigger);
136+
(void) PLy_procedure_get(funcoid, InvalidOid, is_trigger);
137137

138138
PG_RETURN_VOID();
139139
}

src/pl/plpython/plpy_procedure.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ PLy_procedure_name(PLyProcedure *proc)
6060
*
6161
* The reason that both fn_rel and is_trigger need to be passed is that when
6262
* trigger functions get validated we don't know which relation(s) they'll
63-
* be used with, so no sensible fn_rel can be passed.
63+
* be used with, so no sensible fn_rel can be passed. Also, in that case
64+
* we can't make a cache entry because we can't construct the right cache key.
65+
* To forestall leakage of the PLyProcedure in such cases, delete it after
66+
* construction and return NULL. That's okay because the only caller that
67+
* would pass that set of values is plpython3_validator, which ignores our
68+
* result anyway.
6469
*/
6570
PLyProcedure *
6671
PLy_procedure_get(Oid fn_oid, Oid fn_rel, PLyTrigType is_trigger)
@@ -102,6 +107,12 @@ PLy_procedure_get(Oid fn_oid, Oid fn_rel, PLyTrigType is_trigger)
102107
proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
103108
if (use_cache)
104109
entry->proc = proc;
110+
else
111+
{
112+
/* Delete the proc, otherwise it's a memory leak */
113+
PLy_procedure_delete(proc);
114+
proc = NULL;
115+
}
105116
}
106117
else if (!PLy_procedure_valid(proc, procTup))
107118
{

0 commit comments

Comments
 (0)