Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)