Skip to content

Commit 934a24c

Browse files
bpo-46025: Fix a crash in the atexit module for auto-unregistering functions (GH-30002) (GH-30005)
(cherry picked from commit f0d290d) Co-authored-by: Pablo Galindo Salgado <[email protected]> Co-authored-by: Pablo Galindo Salgado <[email protected]>
1 parent 2c2ee83 commit 934a24c

File tree

3 files changed

+21
-1
lines changed

3 files changed

+21
-1
lines changed

Lib/test/_test_atexit.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ 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+
with support.catch_unraisable_exception() as cm:
127+
atexit._run_exitfuncs()
128+
self.assertEqual(cm.unraisable.object, func)
129+
self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError)
130+
self.assertEqual(type(cm.unraisable.exc_value), ZeroDivisionError)
131+
finally:
132+
atexit.unregister(func)
133+
119134

120135
if __name__ == "__main__":
121136
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: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,16 @@ atexit_callfuncs(struct atexit_state *state)
9393
continue;
9494
}
9595

96+
// bpo-46025: Increment the refcount of cb->func as the call itself may unregister it
97+
PyObject* the_func = Py_NewRef(cb->func);
9698
PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs);
9799
if (res == NULL) {
98-
_PyErr_WriteUnraisableMsg("in atexit callback", cb->func);
100+
_PyErr_WriteUnraisableMsg("in atexit callback", the_func);
99101
}
100102
else {
101103
Py_DECREF(res);
102104
}
105+
Py_DECREF(the_func);
103106
}
104107

105108
atexit_cleanup(state);

0 commit comments

Comments
 (0)