Skip to content

Commit 6b3b15e

Browse files
committed
Fix refcounting bug in PLy_modify_tuple().
We must increment the refcount on "plntup" as soon as we have the reference, not sometime later. Otherwise, if an error is thrown in between, the Py_XDECREF(plntup) call in the PG_CATCH block removes a refcount we didn't add, allowing the object to be freed even though it's still part of the plpython function's parsetree. This appears to be the cause of crashes seen on buildfarm member prairiedog. It's a bit surprising that we've not seen it fail repeatably before, considering that the regression tests have been exercising the faulty code path since 2009. The real-world impact is probably minimal, since it's unlikely anyone would be provoking the "TD["new"] is not a dictionary" error in production, and that's the only case that is actually wrong. Still, it's a bug affecting the regression tests, so patch all supported branches. In passing, remove dead variable "plstr", and demote "platt" to a local variable inside the PG_TRY block, since we don't need to clean it up in the PG_CATCH path.
1 parent 6fe8411 commit 6b3b15e

File tree

1 file changed

+3
-5
lines changed

1 file changed

+3
-5
lines changed

src/pl/plpython/plpy_exec.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -634,9 +634,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
634634
{
635635
PyObject *volatile plntup;
636636
PyObject *volatile plkeys;
637-
PyObject *volatile platt;
638637
PyObject *volatile plval;
639-
PyObject *volatile plstr;
640638
HeapTuple rtup;
641639
int natts,
642640
i,
@@ -652,7 +650,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
652650
plerrcontext.previous = error_context_stack;
653651
error_context_stack = &plerrcontext;
654652

655-
plntup = plkeys = platt = plval = plstr = NULL;
653+
plntup = plkeys = plval = NULL;
656654
modattrs = NULL;
657655
modvalues = NULL;
658656
modnulls = NULL;
@@ -662,10 +660,10 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
662660
if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
663661
ereport(ERROR,
664662
(errmsg("TD[\"new\"] deleted, cannot modify row")));
663+
Py_INCREF(plntup);
665664
if (!PyDict_Check(plntup))
666665
ereport(ERROR,
667666
(errmsg("TD[\"new\"] is not a dictionary")));
668-
Py_INCREF(plntup);
669667

670668
plkeys = PyDict_Keys(plntup);
671669
natts = PyList_Size(plkeys);
@@ -678,6 +676,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
678676

679677
for (i = 0; i < natts; i++)
680678
{
679+
PyObject *platt;
681680
char *plattstr;
682681

683682
platt = PyList_GetItem(plkeys, i);
@@ -744,7 +743,6 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
744743
Py_XDECREF(plntup);
745744
Py_XDECREF(plkeys);
746745
Py_XDECREF(plval);
747-
Py_XDECREF(plstr);
748746

749747
if (modnulls)
750748
pfree(modnulls);

0 commit comments

Comments
 (0)