Skip to content

Commit 002b033

Browse files
committed
bpo-46025: Fix a crash in the atexit module for auto-unregistering functions
1 parent 73325bb commit 002b033

File tree

3 files changed

+18
-1
lines changed

3 files changed

+18
-1
lines changed

Lib/test/_test_atexit.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ def test_bound_methods(self):
116116
atexit._run_exitfuncs()
117117
self.assertEqual(l, [5])
118118

119+
def test_atexit_with_unregistered_function(self):
120+
# See bpo-46025 for more info
121+
def func():
122+
atexit.unregister(func)
123+
1/0
124+
atexit.register(func)
125+
try:
126+
atexit._run_exitfuncs()
127+
finally:
128+
atexit.unregister(func)
129+
119130

120131
if __name__ == "__main__":
121132
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash in the :mod:`atexit` module involving functions that unregister
2+
themselves before raising exceptions. Patch by Pablo Galindo.

Modules/atexitmodule.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,17 @@ atexit_callfuncs(struct atexit_state *state)
9393
continue;
9494
}
9595

96+
// Increment the refcount of cb->func as the call itself may unregister it
97+
PyObject* the_func = cb->func;
98+
Py_INCREF(the_func);
9699
PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs);
97100
if (res == NULL) {
98-
_PyErr_WriteUnraisableMsg("in atexit callback", cb->func);
101+
_PyErr_WriteUnraisableMsg("in atexit callback", the_func);
99102
}
100103
else {
101104
Py_DECREF(res);
102105
}
106+
Py_DECREF(the_func);
103107
}
104108

105109
atexit_cleanup(state);

0 commit comments

Comments
 (0)