Avoid Python memory leaks in hstore_plpython and jsonb_plpython.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Apr 2019 21:54:29 +0000 (17:54 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Apr 2019 21:54:29 +0000 (17:54 -0400)
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

contrib/hstore_plpython/hstore_plpython.c
contrib/jsonb_plpython/jsonb_plpython.c

index 93c39d294dd439f4fa7752283fe643ac4a4d2dc6..93705f0d54cb4b809261bf1d80f694a49976de1b 100644 (file)
@@ -128,9 +128,9 @@ Datum
 plpython_to_hstore(PG_FUNCTION_ARGS)
 {
        PyObject   *dict;
-       PyObject *volatile items = NULL;
-       int32           pcount;
-       HStore     *out;
+       PyObject   *volatile items;
+       Py_ssize_t      pcount;
+       HStore     *volatile out;
 
        dict = (PyObject *) PG_GETARG_POINTER(0);
        if (!PyMapping_Check(dict))
@@ -144,7 +144,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
        PG_TRY();
        {
                int32           buflen;
-               int32           i;
+               Py_ssize_t      i;
                Pairs      *pairs;
 
                pairs = palloc(pcount * sizeof(*pairs));
@@ -176,7 +176,6 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
                                pairs[i].isnull = false;
                        }
                }
-               Py_DECREF(items);
 
                pcount = hstoreUniquePairs(pairs, pcount, &buflen);
                out = hstorePairs(pairs, pcount, buflen);
@@ -188,5 +187,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
        }
        PG_END_TRY();
 
+       Py_DECREF(items);
+
        PG_RETURN_POINTER(out);
 }
index 1bc984d5c4d9dbaacaa3675c2da2d7d0e8f4b657..24e7c6559d63fb9b77141c427625df835f261330 100644 (file)
@@ -164,56 +164,91 @@ PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
                        }
                        else
                        {
-                               /* array in v */
+                               PyObject   *volatile elem = NULL;
+
                                result = PyList_New(0);
                                if (!result)
                                        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);
                                                Py_XDECREF(elem);
+                                               elem = NULL;
                                        }
                                }
+                               PG_CATCH();
+                               {
+                                       Py_XDECREF(elem);
+                                       Py_XDECREF(result);
+                                       PG_RE_THROW();
+                               }
+                               PG_END_TRY();
                        }
                        break;
 
                case WJB_BEGIN_OBJECT:
-                       result = PyDict_New();
-                       if (!result)
-                               return NULL;
-
-                       while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
                        {
-                               if (r == WJB_KEY)
-                               {
-                                       PyObject   *key = PLyString_FromJsonbValue(&v);
+                               PyObject   *volatile result_v = PyDict_New();
+                               PyObject   *volatile key = NULL;
+                               PyObject   *volatile val = NULL;
 
-                                       if (!key)
-                                               return NULL;
-
-                                       r = JsonbIteratorNext(&it, &v, true);
+                               if (!result_v)
+                                       return NULL;
 
-                                       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);
-                                                       return NULL;
+                                                       key = NULL;
+                                                       Py_XDECREF(result_v);
+                                                       result_v = NULL;
+                                                       break;
                                                }
 
-                                               PyDict_SetItem(result, key, value);
-                                               Py_XDECREF(value);
-                                       }
+                                               PyDict_SetItem(result_v, key, val);
 
+                                               Py_XDECREF(key);
+                                               key = NULL;
+                                               Py_XDECREF(val);
+                                               val = NULL;
+                                       }
+                               }
+                               PG_CATCH();
+                               {
+                                       Py_XDECREF(result_v);
                                        Py_XDECREF(key);
+                                       Py_XDECREF(val);
+                                       PG_RE_THROW();
                                }
+                               PG_END_TRY();
+
+                               result = result_v;
                        }
                        break;
 
@@ -234,10 +269,8 @@ static JsonbValue *
 PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
 {
        Py_ssize_t      pcount;
-       JsonbValue *out = NULL;
-
-       /* We need it volatile, since we use it after longjmp */
-       PyObject *volatile items = NULL;
+       PyObject   *volatile items;
+       JsonbValue *volatile out;
 
        pcount = PyMapping_Size(obj);
        items = PyMapping_Items(obj);
@@ -281,6 +314,8 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
        }
        PG_END_TRY();
 
+       Py_DECREF(items);
+
        return out;
 }
 
@@ -295,19 +330,30 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
 {
        Py_ssize_t      i;
        Py_ssize_t      pcount;
+       PyObject   *volatile value = NULL;
 
        pcount = PySequence_Size(obj);
 
        pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
 
-       for (i = 0; i < pcount; i++)
+       PG_TRY();
        {
-               PyObject   *value = PySequence_GetItem(obj, i);
-
-               (void) PLyObject_ToJsonbValue(value, jsonb_state, true);
+               for (i = 0; i < pcount; i++)
+               {
+                       value = PySequence_GetItem(obj, i);
+                       Assert(value);
 
+                       (void) PLyObject_ToJsonbValue(value, jsonb_state, true);
+                       Py_XDECREF(value);
+                       value = NULL;
+               }
+       }
+       PG_CATCH();
+       {
                Py_XDECREF(value);
+               PG_RE_THROW();
        }
+       PG_END_TRY();
 
        return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
 }