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