From 8728018624f257c7cfe44014742ae46134047f49 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 11 Sep 2017 17:59:22 -0700 Subject: bpo-30860: Fix a refleak. (#3506) * Drop warnoptions from PyInterpreterState. * Drop xoptions from PyInterpreterState. * Don't set warnoptions and _xoptions again. * Decref after adding to sys.__dict__. * Drop an unused macro. * Check sys.xoptions *before* we delete it. --- Include/object.h | 3 +-- Include/pystate.h | 2 -- Objects/object.c | 27 +++++++++++++++------------ Python/pylifecycle.c | 10 +++++++++- Python/pystate.c | 2 -- Python/pythonrun.c | 5 ++++- Python/sysmodule.c | 41 ++++++++++++++++++----------------------- 7 files changed, 47 insertions(+), 43 deletions(-) diff --git a/Include/object.h b/Include/object.h index b46d4c3..9bb780e 100644 --- a/Include/object.h +++ b/Include/object.h @@ -727,14 +727,13 @@ PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void); /* Py_REF_DEBUG also controls the display of refcounts and memory block * allocations at the interactive prompt and at interpreter shutdown */ +PyAPI_FUNC(PyObject *) _PyDebug_XOptionShowRefCount(void); PyAPI_FUNC(void) _PyDebug_PrintTotalRefs(void); -#define _PY_DEBUG_PRINT_TOTAL_REFS() _PyDebug_PrintTotalRefs() #else #define _Py_INC_REFTOTAL #define _Py_DEC_REFTOTAL #define _Py_REF_DEBUG_COMMA #define _Py_CHECK_REFCNT(OP) /* a semicolon */; -#define _PY_DEBUG_PRINT_TOTAL_REFS() #endif /* Py_REF_DEBUG */ #ifdef COUNT_ALLOCS diff --git a/Include/pystate.h b/Include/pystate.h index d986e35..5b75bb0 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -60,8 +60,6 @@ typedef struct _is { /* Used in Python/sysmodule.c. */ int check_interval; - PyObject *warnoptions; - PyObject *xoptions; /* Used in Modules/_threadmodule.c. */ long num_threads; diff --git a/Objects/object.c b/Objects/object.c index 74893e3..ed8a62a 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -29,20 +29,23 @@ _Py_GetRefTotal(void) return total; } -void -_PyDebug_PrintTotalRefs(void) { - PyObject *xoptions, *value; +PyObject * +_PyDebug_XOptionShowRefCount(void) +{ + PyObject *xoptions = PySys_GetXOptions(); + if (xoptions == NULL) + return NULL; + _Py_IDENTIFIER(showrefcount); + return _PyDict_GetItemId(xoptions, &PyId_showrefcount); +} - xoptions = PySys_GetXOptions(); - if (xoptions == NULL) - return; - value = _PyDict_GetItemId(xoptions, &PyId_showrefcount); - if (value == Py_True) - fprintf(stderr, - "[%" PY_FORMAT_SIZE_T "d refs, " - "%" PY_FORMAT_SIZE_T "d blocks]\n", - _Py_GetRefTotal(), _Py_GetAllocatedBlocks()); +void +_PyDebug_PrintTotalRefs(void) { + fprintf(stderr, + "[%" PY_FORMAT_SIZE_T "d refs, " + "%" PY_FORMAT_SIZE_T "d blocks]\n", + _Py_GetRefTotal(), _Py_GetAllocatedBlocks()); } #endif /* Py_REF_DEBUG */ diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index caa324e..3265d70 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1006,6 +1006,11 @@ Py_FinalizeEx(void) while (_PyGC_CollectIfEnabled() > 0) /* nothing */; #endif + +#ifdef Py_REF_DEBUG + PyObject *showrefcount = _PyDebug_XOptionShowRefCount(); +#endif + /* Destroy all modules */ PyImport_Cleanup(); @@ -1053,7 +1058,10 @@ Py_FinalizeEx(void) /* dump hash stats */ _PyHash_Fini(); - _PY_DEBUG_PRINT_TOTAL_REFS(); +#ifdef Py_REF_DEBUG + if (showrefcount == Py_True) + _PyDebug_PrintTotalRefs(); +#endif #ifdef Py_TRACE_REFS /* Display all objects still alive -- this can invoke arbitrary diff --git a/Python/pystate.c b/Python/pystate.c index be012d7..0804842 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -96,8 +96,6 @@ PyInterpreterState_New(void) interp->builtins_copy = NULL; interp->tstate_head = NULL; interp->check_interval = 100; - interp->warnoptions = NULL; - interp->xoptions = NULL; interp->num_threads = 0; interp->pythread_stacksize = 0; interp->codec_search_path = NULL; diff --git a/Python/pythonrun.c b/Python/pythonrun.c index d1d4a69..df814fb 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -113,7 +113,10 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char *filename_str, PyCompilerFlags * err = -1; for (;;) { ret = PyRun_InteractiveOneObject(fp, filename, flags); - _PY_DEBUG_PRINT_TOTAL_REFS(); +#ifdef Py_REF_DEBUG + if (_PyDebug_XOptionShowRefCount() == Py_True) + _PyDebug_PrintTotalRefs(); +#endif if (ret == E_EOF) { err = 0; break; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 3ecd7fc..5bde4b7 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -36,12 +36,14 @@ extern const char *PyWin_DLLVersionString; _Py_IDENTIFIER(_); _Py_IDENTIFIER(__sizeof__); +_Py_IDENTIFIER(_xoptions); _Py_IDENTIFIER(buffer); _Py_IDENTIFIER(builtins); _Py_IDENTIFIER(encoding); _Py_IDENTIFIER(path); _Py_IDENTIFIER(stdout); _Py_IDENTIFIER(stderr); +_Py_IDENTIFIER(warnoptions); _Py_IDENTIFIER(write); PyObject * @@ -1479,13 +1481,17 @@ list_builtin_module_names(void) static PyObject * get_warnoptions(void) { - PyObject *warnoptions = PyThreadState_GET()->interp->warnoptions; + PyObject *warnoptions = _PySys_GetObjectId(&PyId_warnoptions); if (warnoptions == NULL || !PyList_Check(warnoptions)) { Py_XDECREF(warnoptions); warnoptions = PyList_New(0); if (warnoptions == NULL) return NULL; - PyThreadState_GET()->interp->warnoptions = warnoptions; + if (_PySys_SetObjectId(&PyId_warnoptions, warnoptions)) { + Py_DECREF(warnoptions); + return NULL; + } + Py_DECREF(warnoptions); } return warnoptions; } @@ -1493,7 +1499,7 @@ get_warnoptions(void) void PySys_ResetWarnOptions(void) { - PyObject *warnoptions = PyThreadState_GET()->interp->warnoptions; + PyObject *warnoptions = _PySys_GetObjectId(&PyId_warnoptions); if (warnoptions == NULL || !PyList_Check(warnoptions)) return; PyList_SetSlice(warnoptions, 0, PyList_GET_SIZE(warnoptions), NULL); @@ -1522,20 +1528,24 @@ PySys_AddWarnOption(const wchar_t *s) int PySys_HasWarnOptions(void) { - PyObject *warnoptions = PyThreadState_GET()->interp->warnoptions; + PyObject *warnoptions = _PySys_GetObjectId(&PyId_warnoptions); return (warnoptions != NULL && (PyList_Size(warnoptions) > 0)) ? 1 : 0; } static PyObject * get_xoptions(void) { - PyObject *xoptions = PyThreadState_GET()->interp->xoptions; + PyObject *xoptions = _PySys_GetObjectId(&PyId__xoptions); if (xoptions == NULL || !PyDict_Check(xoptions)) { Py_XDECREF(xoptions); xoptions = PyDict_New(); if (xoptions == NULL) return NULL; - PyThreadState_GET()->interp->xoptions = xoptions; + if (_PySys_SetObjectId(&PyId__xoptions, xoptions)) { + Py_DECREF(xoptions); + return NULL; + } + Py_DECREF(xoptions); } return xoptions; } @@ -2084,16 +2094,6 @@ _PySys_BeginInit(void) #undef SET_SYS_FROM_STRING_BORROW /* Updating the sys namespace, returning integer error codes */ -#define SET_SYS_FROM_STRING_BORROW_INT_RESULT(key, value) \ - do { \ - PyObject *v = (value); \ - if (v == NULL) \ - return -1; \ - res = PyDict_SetItemString(sysdict, key, v); \ - if (res < 0) { \ - return res; \ - } \ - } while (0) #define SET_SYS_FROM_STRING_INT_RESULT(key, value) \ do { \ PyObject *v = (value); \ @@ -2138,15 +2138,11 @@ _PySys_EndInit(PyObject *sysdict) SET_SYS_FROM_STRING_INT_RESULT("base_exec_prefix", PyUnicode_FromWideChar(Py_GetExecPrefix(), -1)); - PyObject *warnoptions = get_warnoptions(); - if (warnoptions == NULL) + if (get_warnoptions() == NULL) return -1; - SET_SYS_FROM_STRING_BORROW_INT_RESULT("warnoptions", warnoptions); - PyObject *xoptions = get_xoptions(); - if (xoptions == NULL) + if (get_xoptions() == NULL) return -1; - SET_SYS_FROM_STRING_BORROW_INT_RESULT("_xoptions", xoptions); if (PyErr_Occurred()) return -1; @@ -2154,7 +2150,6 @@ _PySys_EndInit(PyObject *sysdict) } #undef SET_SYS_FROM_STRING_INT_RESULT -#undef SET_SYS_FROM_STRING_BORROW_INT_RESULT static PyObject * makepathobject(const wchar_t *path, wchar_t delim) -- cgit v0.12