Thread
Commits
-
Fix resource leaks in PL/Python error reporting, redux.
- fbc41a145ae0 17.7 landed
- d90c92d1c16d 13.23 landed
- cbfd4d0f883d 16.11 landed
- 7fb8c924042e 14.20 landed
- 798b19d27b28 19 (unreleased) landed
- 4cde7325924e 15.15 landed
- 447a794f6473 18.1 landed
-
Fix edge-case resource leaks in PL/Python error reporting.
- c6f7f11d8f4c 18.0 cited
-
Memory leak due to thinko in PL/Python error handling
Tom Lane <tgl@sss.pgh.pa.us> — 2025-10-23T01:49:19Z
I have realized that there's an oversight in my recent commit c6f7f11d8, which so far from preventing edge-case leaks as intended, actually introduces a memory leak in the normal error-catching path. The leakage is easy to see if you extract the catch_python_unique_violation() test case from plpython_error.sql and run it in a loop, like pl_regression=# do $$ begin for i in 1..1000000 loop perform catch_python_unique_violation(); end loop; end$$; The backend's VIRT size as reported by top(1) will grow steadily. The problem is that I did not think through the fact that PyObject_GetAttrString() acquires a refcount on the returned object, so that after advancing to the next frame object with tb = PyObject_GetAttrString(tb, "tb_next"); we have an extra refcount on the new "tb" object, and there's no logic that will get rid of that. The loop that I introduced with an eye to cleaning things up: /* Must release all the objects in the traceback stack */ while (tb != NULL && tb != Py_None) { PyObject *tb_prev = tb; tb = PyObject_GetAttrString(tb, "tb_next"); Py_DECREF(tb_prev); } isn't right either, since it likewise doesn't account for the extra refcount added by PyObject_GetAttrString. The correct fix, I believe, is as attached. If we avoid collecting extra refcounts during PLy_traceback(), then PLy_elog_impl() can go back to simply doing Py_XDECREF(tb) on the first frame. Any additional frames will go away when the previous frame object is cleaned up and drops its refcount. I also added a couple of comments explaining why PLy_elog_impl() doesn't try to free the strings acquired from PLy_get_spi_error_data() or PLy_get_error_data(). That's because I got here by looking at a Coverity complaint about how those strings might get leaked. They are not leaked, but in testing that I discovered this other leak. regards, tom lane