Skip to content

Commit 2b466c4

Browse files
gh-112127: Fix possible use-after-free in atexit.unregister() (GH-114092)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 49627dc commit 2b466c4

File tree

4 files changed

+18
-1
lines changed

4 files changed

+18
-1
lines changed

Lib/test/_test_atexit.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,19 @@ def func():
135135
finally:
136136
atexit.unregister(func)
137137

138+
def test_eq_unregister_clear(self):
139+
# Issue #112127: callback's __eq__ may call unregister or _clear
140+
class Evil:
141+
def __eq__(self, other):
142+
action(other)
143+
return NotImplemented
144+
145+
for action in atexit.unregister, lambda o: atexit._clear():
146+
with self.subTest(action=action):
147+
atexit.register(lambda: None)
148+
atexit.unregister(Evil())
149+
atexit._clear()
150+
138151

139152
if __name__ == "__main__":
140153
unittest.main()

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ Jim Jewett
908908
Pedro Diaz Jimenez
909909
Orjan Johansen
910910
Fredrik Johansson
911+
Benjamin Johnson
911912
Benjamin K. Johnson
912913
Gregory K. Johnson
913914
Kent Johnson
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix possible use-after-free in :func:`atexit.unregister` when the callback
2+
is unregistered during comparison.

Modules/atexitmodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,11 @@ static int
257257
atexit_unregister_locked(PyObject *callbacks, PyObject *func)
258258
{
259259
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) {
260-
PyObject *tuple = PyList_GET_ITEM(callbacks, i);
260+
PyObject *tuple = Py_NewRef(PyList_GET_ITEM(callbacks, i));
261261
assert(PyTuple_CheckExact(tuple));
262262
PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0);
263263
int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ);
264+
Py_DECREF(tuple);
264265
if (cmp < 0)
265266
{
266267
return -1;

0 commit comments

Comments
 (0)