Re: Fix memleaks and error handling in jsonb_plpython

From: Nikita Glukhov <n(dot)gluhov(at)postgrespro(dot)ru>
To: Michael Paquier <michael(at)paquier(dot)xyz>
Cc: PostgreSQL mailing lists <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Fix memleaks and error handling in jsonb_plpython
Date: 2019-03-05 11:10:01
Message-ID: fa61eb4a-43be-0573-764d-ccd244688217@postgrespro.ru
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 05.03.2019 6:45, Michael Paquier wrote:

> On Fri, Mar 01, 2019 at 05:24:39AM +0300, Nikita Glukhov wrote:
>> Unfortunately, contrib/jsonb_plpython still contain a lot of problems in error
>> handling that can lead to memory leaks:
>> - not all Python function calls are checked for the success
>> - not in all places PG exceptions are caught to release Python references
>> But it seems that this errors can happen only in OOM case.
>>
>> Attached patch with the fix. Back-patch for PG11 is needed.
> That looks right to me. Here are some comments.
>
> One thing to be really careful of when using PG_TRY/PG_CATCH blocks is
> that variables modified in the try block and then referenced in the
> catch block need to be marked as volatile. If you don't do that, the
> value when reaching the catch part is indeterminate.
>
> With your patch the result variable used in two places of
> PLyObject_FromJsonbContainer() is not marked as volatile. Similarly,
> it seems that "items" in PLyMapping_ToJsonbValue() and "seq" in
> "PLySequence_ToJsonbValue" should be volatile because they get changed
> in the try loop, and referenced afterwards.

I known about this volatility issues, but maybe I incorrectly understand what
should be marked as volatile for pointer variables: the pointer itself and/or
the memory referenced by it. I thought that only pointer needs to be marked,
and also there is message [1] clearly describing what needs to be marked.

Previously in PLyMapping_ToJsonbValue() the whole contents of PyObject was
marked as volatile, not the pointer itself which is not modified in PG_TRY:

- /* We need it volatile, since we use it after longjmp */
- volatile PyObject *items_v = NULL;

So, I removed volatile qualifier here.

Variable 'result' is also not modified in PG_TRY, it is also non-volatile.

I marked only 'key' variable in PLyObject_FromJsonbContainer() as volatile,
because it is really modified in the loop inside PG_TRY(), and
PLyObject_FromJsonbValue(&v2) call after its assignment can throw PG
exception:
+ PyObject *volatile key = NULL;

Also I have idea to introduce a global list of Python objects that need to be
dereferenced in PG_CATCH inside PLy_exec_function() in the case of exception.
Then typical code will be look like that:

PyObject *list = PLy_RegisterObject(PyList_New());

if (!list)
return NULL;

... code that can throw PG exception, PG_TRY/PG_CATCH is not needed ...

return PLy_UnregisterObject(list); /* returns list */

> Another issue: in ltree_plpython we don't check the return state of
> PyList_SetItem(), which we should complain about I think.

Yes, PyList_SetItem() and PyString_FromStringAndSize() should be checked,
but CPython's PyList_SetItem() really should not fail because list storage
is preallocated:

int
PyList_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem)
{
PyObject **p;
if (!PyList_Check(op)) {
Py_XDECREF(newitem);
PyErr_BadInternalCall();
return -1;
}
if (!valid_index(i, Py_SIZE(op))) {
Py_XDECREF(newitem);
PyErr_SetString(PyExc_IndexError,
"list assignment index out of range");
return -1;
}
p = ((PyListObject *)op) -> ob_item + i;
Py_XSETREF(*p, newitem);
return 0;
}

[1] https://www.postgresql.org/message-id/31436.1483415248%40sss.pgh.pa.us

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Thomas Munro 2019-03-05 11:15:18 Re: Rare SSL failures on eelpout
Previous Message Kyotaro HORIGUCHI 2019-03-05 11:01:21 Re: New vacuum option to do only freezing