Bug description
atexit_register() in Modules/atexitmodule.c leaks memory on every call. PyTuple_GetSlice() returns a new reference, but the result is never decref'd after being packed into the callback tuple via PyTuple_Pack().
Additionally, there is no NULL check after PyTuple_GetSlice(), which could lead to undefined behavior if the allocation fails.
// Modules/atexitmodule.c, atexit_register()
PyObject *func_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
// BUG 1: missing NULL check
PyObject *func_kwargs = kwargs;
...
PyObject *callback = PyTuple_Pack(3, func, func_args, func_kwargs);
// BUG 2: missing Py_DECREF(func_args) — leaks the tuple
The leak is detected by AddressSanitizer/LeakSanitizer when running test_atexit on an ASAN build, causing 3 test failures: test_shutdown, test_atexit_instances, and test_general (subprocess exits with code 1 due to LSAN reporting 240 bytes leaked in 4 allocations).
Fix:
PyObject *func_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
if (func_args == NULL) {
return NULL;
}
...
PyObject *callback = PyTuple_Pack(3, func, func_args, func_kwargs);
Py_DECREF(func_args);
CPython versions tested on
CPython main branch
Operating systems tested on
Linux
Linked PRs
Bug description
atexit_register()inModules/atexitmodule.cleaks memory on every call.PyTuple_GetSlice()returns a new reference, but the result is never decref'd after being packed into the callback tuple viaPyTuple_Pack().Additionally, there is no NULL check after
PyTuple_GetSlice(), which could lead to undefined behavior if the allocation fails.The leak is detected by AddressSanitizer/LeakSanitizer when running
test_atexiton an ASAN build, causing 3 test failures:test_shutdown,test_atexit_instances, andtest_general(subprocess exits with code 1 due to LSAN reporting 240 bytes leaked in 4 allocations).Fix:
CPython versions tested on
CPython main branch
Operating systems tested on
Linux
Linked PRs