gh-112127: Fix possible use-after-free in atexit.unregister() (GH-114092)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Benjamin Johnson 2025-12-17 07:09:57 -08:00 committed by GitHub
parent 49627dc991
commit 2b466c47c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 18 additions and 1 deletions

View file

@ -135,6 +135,19 @@ def func():
finally:
atexit.unregister(func)
def test_eq_unregister_clear(self):
# Issue #112127: callback's __eq__ may call unregister or _clear
class Evil:
def __eq__(self, other):
action(other)
return NotImplemented
for action in atexit.unregister, lambda o: atexit._clear():
with self.subTest(action=action):
atexit.register(lambda: None)
atexit.unregister(Evil())
atexit._clear()
if __name__ == "__main__":
unittest.main()

View file

@ -908,6 +908,7 @@ Jim Jewett
Pedro Diaz Jimenez
Orjan Johansen
Fredrik Johansson
Benjamin Johnson
Benjamin K. Johnson
Gregory K. Johnson
Kent Johnson

View file

@ -0,0 +1,2 @@
Fix possible use-after-free in :func:`atexit.unregister` when the callback
is unregistered during comparison.

View file

@ -257,10 +257,11 @@ static int
atexit_unregister_locked(PyObject *callbacks, PyObject *func)
{
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) {
PyObject *tuple = PyList_GET_ITEM(callbacks, i);
PyObject *tuple = Py_NewRef(PyList_GET_ITEM(callbacks, i));
assert(PyTuple_CheckExact(tuple));
PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0);
int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ);
Py_DECREF(tuple);
if (cmp < 0)
{
return -1;