Skip to content

Commit 9e360f0

Browse files
committed
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
1 parent 46e3442 commit 9e360f0

File tree

2 files changed

+83
-36
lines changed

2 files changed

+83
-36
lines changed

contrib/hstore_plpython/hstore_plpython.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ Datum
128128
plpython_to_hstore(PG_FUNCTION_ARGS)
129129
{
130130
PyObject *dict;
131-
PyObject *volatile items = NULL;
132-
int32 pcount;
133-
HStore *out;
131+
PyObject *volatile items;
132+
Py_ssize_t pcount;
133+
HStore *volatile out;
134134

135135
dict = (PyObject *) PG_GETARG_POINTER(0);
136136
if (!PyMapping_Check(dict))
@@ -144,7 +144,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
144144
PG_TRY();
145145
{
146146
int32 buflen;
147-
int32 i;
147+
Py_ssize_t i;
148148
Pairs *pairs;
149149

150150
pairs = palloc(pcount * sizeof(*pairs));
@@ -176,7 +176,6 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
176176
pairs[i].isnull = false;
177177
}
178178
}
179-
Py_DECREF(items);
180179

181180
pcount = hstoreUniquePairs(pairs, pcount, &buflen);
182181
out = hstorePairs(pairs, pcount, buflen);
@@ -188,5 +187,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
188187
}
189188
PG_END_TRY();
190189

190+
Py_DECREF(items);
191+
191192
PG_RETURN_POINTER(out);
192193
}

contrib/jsonb_plpython/jsonb_plpython.c

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -164,56 +164,91 @@ PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
164164
}
165165
else
166166
{
167-
/* array in v */
167+
PyObject *volatile elem = NULL;
168+
168169
result = PyList_New(0);
169170
if (!result)
170171
return NULL;
171172

172-
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
173+
PG_TRY();
173174
{
174-
if (r == WJB_ELEM)
175+
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
175176
{
176-
PyObject *elem = PLyObject_FromJsonbValue(&v);
177+
if (r != WJB_ELEM)
178+
continue;
179+
180+
elem = PLyObject_FromJsonbValue(&v);
177181

178182
PyList_Append(result, elem);
179183
Py_XDECREF(elem);
184+
elem = NULL;
180185
}
181186
}
187+
PG_CATCH();
188+
{
189+
Py_XDECREF(elem);
190+
Py_XDECREF(result);
191+
PG_RE_THROW();
192+
}
193+
PG_END_TRY();
182194
}
183195
break;
184196

185197
case WJB_BEGIN_OBJECT:
186-
result = PyDict_New();
187-
if (!result)
188-
return NULL;
189-
190-
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
191198
{
192-
if (r == WJB_KEY)
193-
{
194-
PyObject *key = PLyString_FromJsonbValue(&v);
199+
PyObject *volatile result_v = PyDict_New();
200+
PyObject *volatile key = NULL;
201+
PyObject *volatile val = NULL;
195202

196-
if (!key)
197-
return NULL;
198-
199-
r = JsonbIteratorNext(&it, &v, true);
203+
if (!result_v)
204+
return NULL;
200205

201-
if (r == WJB_VALUE)
206+
PG_TRY();
207+
{
208+
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
202209
{
203-
PyObject *value = PLyObject_FromJsonbValue(&v);
210+
if (r != WJB_KEY)
211+
continue;
204212

205-
if (!value)
213+
key = PLyString_FromJsonbValue(&v);
214+
if (!key)
215+
{
216+
Py_XDECREF(result_v);
217+
result_v = NULL;
218+
break;
219+
}
220+
221+
if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_VALUE)
222+
elog(ERROR, "unexpected jsonb token: %d", r);
223+
224+
val = PLyObject_FromJsonbValue(&v);
225+
if (!val)
206226
{
207227
Py_XDECREF(key);
208-
return NULL;
228+
key = NULL;
229+
Py_XDECREF(result_v);
230+
result_v = NULL;
231+
break;
209232
}
210233

211-
PyDict_SetItem(result, key, value);
212-
Py_XDECREF(value);
213-
}
234+
PyDict_SetItem(result_v, key, val);
214235

236+
Py_XDECREF(key);
237+
key = NULL;
238+
Py_XDECREF(val);
239+
val = NULL;
240+
}
241+
}
242+
PG_CATCH();
243+
{
244+
Py_XDECREF(result_v);
215245
Py_XDECREF(key);
246+
Py_XDECREF(val);
247+
PG_RE_THROW();
216248
}
249+
PG_END_TRY();
250+
251+
result = result_v;
217252
}
218253
break;
219254

@@ -234,10 +269,8 @@ static JsonbValue *
234269
PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
235270
{
236271
Py_ssize_t pcount;
237-
JsonbValue *out = NULL;
238-
239-
/* We need it volatile, since we use it after longjmp */
240-
PyObject *volatile items = NULL;
272+
PyObject *volatile items;
273+
JsonbValue *volatile out;
241274

242275
pcount = PyMapping_Size(obj);
243276
items = PyMapping_Items(obj);
@@ -281,6 +314,8 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
281314
}
282315
PG_END_TRY();
283316

317+
Py_DECREF(items);
318+
284319
return out;
285320
}
286321

@@ -295,19 +330,30 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
295330
{
296331
Py_ssize_t i;
297332
Py_ssize_t pcount;
333+
PyObject *volatile value = NULL;
298334

299335
pcount = PySequence_Size(obj);
300336

301337
pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
302338

303-
for (i = 0; i < pcount; i++)
339+
PG_TRY();
304340
{
305-
PyObject *value = PySequence_GetItem(obj, i);
306-
307-
(void) PLyObject_ToJsonbValue(value, jsonb_state, true);
341+
for (i = 0; i < pcount; i++)
342+
{
343+
value = PySequence_GetItem(obj, i);
344+
Assert(value);
308345

346+
(void) PLyObject_ToJsonbValue(value, jsonb_state, true);
347+
Py_XDECREF(value);
348+
value = NULL;
349+
}
350+
}
351+
PG_CATCH();
352+
{
309353
Py_XDECREF(value);
354+
PG_RE_THROW();
310355
}
356+
PG_END_TRY();
311357

312358
return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
313359
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy