Commit 9e360f0e authored by Tom Lane's avatar Tom Lane

Avoid Python memory leaks in hstore_plpython and jsonb_plpython.

Fix some places where we might fail to do Py_DECREF() on a Python
object (thereby leaking it for the rest of the session).  Almost
all of the risks were in error-recovery paths, which we don't really
expect to hit anyway.  Hence, while this is definitely a bug fix,
it doesn't quite seem worth back-patching.

Nikita Glukhov, Michael Paquier, Tom Lane

Discussion: https://postgr.es/m/28053a7d-10d8-fc23-b05c-b4749c873f63@postgrespro.ru
parent 46e3442c
...@@ -128,9 +128,9 @@ Datum ...@@ -128,9 +128,9 @@ Datum
plpython_to_hstore(PG_FUNCTION_ARGS) plpython_to_hstore(PG_FUNCTION_ARGS)
{ {
PyObject *dict; PyObject *dict;
PyObject *volatile items = NULL; PyObject *volatile items;
int32 pcount; Py_ssize_t pcount;
HStore *out; HStore *volatile out;
dict = (PyObject *) PG_GETARG_POINTER(0); dict = (PyObject *) PG_GETARG_POINTER(0);
if (!PyMapping_Check(dict)) if (!PyMapping_Check(dict))
...@@ -144,7 +144,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS) ...@@ -144,7 +144,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
PG_TRY(); PG_TRY();
{ {
int32 buflen; int32 buflen;
int32 i; Py_ssize_t i;
Pairs *pairs; Pairs *pairs;
pairs = palloc(pcount * sizeof(*pairs)); pairs = palloc(pcount * sizeof(*pairs));
...@@ -176,7 +176,6 @@ plpython_to_hstore(PG_FUNCTION_ARGS) ...@@ -176,7 +176,6 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
pairs[i].isnull = false; pairs[i].isnull = false;
} }
} }
Py_DECREF(items);
pcount = hstoreUniquePairs(pairs, pcount, &buflen); pcount = hstoreUniquePairs(pairs, pcount, &buflen);
out = hstorePairs(pairs, pcount, buflen); out = hstorePairs(pairs, pcount, buflen);
...@@ -188,5 +187,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS) ...@@ -188,5 +187,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
} }
PG_END_TRY(); PG_END_TRY();
Py_DECREF(items);
PG_RETURN_POINTER(out); PG_RETURN_POINTER(out);
} }
...@@ -164,56 +164,91 @@ PLyObject_FromJsonbContainer(JsonbContainer *jsonb) ...@@ -164,56 +164,91 @@ PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
} }
else else
{ {
/* array in v */ PyObject *volatile elem = NULL;
result = PyList_New(0); result = PyList_New(0);
if (!result) if (!result)
return NULL; return NULL;
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) PG_TRY();
{ {
if (r == WJB_ELEM) while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
{ {
PyObject *elem = PLyObject_FromJsonbValue(&v); if (r != WJB_ELEM)
continue;
elem = PLyObject_FromJsonbValue(&v);
PyList_Append(result, elem); PyList_Append(result, elem);
Py_XDECREF(elem); Py_XDECREF(elem);
elem = NULL;
} }
} }
PG_CATCH();
{
Py_XDECREF(elem);
Py_XDECREF(result);
PG_RE_THROW();
}
PG_END_TRY();
} }
break; break;
case WJB_BEGIN_OBJECT: case WJB_BEGIN_OBJECT:
result = PyDict_New();
if (!result)
return NULL;
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
{ {
if (r == WJB_KEY) PyObject *volatile result_v = PyDict_New();
{ PyObject *volatile key = NULL;
PyObject *key = PLyString_FromJsonbValue(&v); PyObject *volatile val = NULL;
if (!key) if (!result_v)
return NULL; return NULL;
r = JsonbIteratorNext(&it, &v, true);
if (r == WJB_VALUE) PG_TRY();
{
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
{ {
PyObject *value = PLyObject_FromJsonbValue(&v); if (r != WJB_KEY)
continue;
if (!value) key = PLyString_FromJsonbValue(&v);
if (!key)
{
Py_XDECREF(result_v);
result_v = NULL;
break;
}
if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_VALUE)
elog(ERROR, "unexpected jsonb token: %d", r);
val = PLyObject_FromJsonbValue(&v);
if (!val)
{ {
Py_XDECREF(key); Py_XDECREF(key);
return NULL; key = NULL;
Py_XDECREF(result_v);
result_v = NULL;
break;
} }
PyDict_SetItem(result, key, value); PyDict_SetItem(result_v, key, val);
Py_XDECREF(value);
}
Py_XDECREF(key);
key = NULL;
Py_XDECREF(val);
val = NULL;
}
}
PG_CATCH();
{
Py_XDECREF(result_v);
Py_XDECREF(key); Py_XDECREF(key);
Py_XDECREF(val);
PG_RE_THROW();
} }
PG_END_TRY();
result = result_v;
} }
break; break;
...@@ -234,10 +269,8 @@ static JsonbValue * ...@@ -234,10 +269,8 @@ static JsonbValue *
PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state) PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
{ {
Py_ssize_t pcount; Py_ssize_t pcount;
JsonbValue *out = NULL; PyObject *volatile items;
JsonbValue *volatile out;
/* We need it volatile, since we use it after longjmp */
PyObject *volatile items = NULL;
pcount = PyMapping_Size(obj); pcount = PyMapping_Size(obj);
items = PyMapping_Items(obj); items = PyMapping_Items(obj);
...@@ -281,6 +314,8 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state) ...@@ -281,6 +314,8 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
} }
PG_END_TRY(); PG_END_TRY();
Py_DECREF(items);
return out; return out;
} }
...@@ -295,19 +330,30 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state) ...@@ -295,19 +330,30 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
{ {
Py_ssize_t i; Py_ssize_t i;
Py_ssize_t pcount; Py_ssize_t pcount;
PyObject *volatile value = NULL;
pcount = PySequence_Size(obj); pcount = PySequence_Size(obj);
pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL); pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
for (i = 0; i < pcount; i++) PG_TRY();
{ {
PyObject *value = PySequence_GetItem(obj, i); for (i = 0; i < pcount; i++)
{
(void) PLyObject_ToJsonbValue(value, jsonb_state, true); value = PySequence_GetItem(obj, i);
Assert(value);
(void) PLyObject_ToJsonbValue(value, jsonb_state, true);
Py_XDECREF(value);
value = NULL;
}
}
PG_CATCH();
{
Py_XDECREF(value); Py_XDECREF(value);
PG_RE_THROW();
} }
PG_END_TRY();
return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL); return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment