diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index 1f89986c777ced8..68c8aadeb50823b 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -2,6 +2,7 @@ import inspect import pickle import sys +import weakref from decimal import Decimal from fractions import Fraction @@ -511,6 +512,21 @@ def return_arguments(self, *args, **kwds): f = operator.methodcaller('return_arguments', *many_positional_arguments, **many_kw_arguments) self.assertEqual(f(a), (many_positional_arguments, many_kw_arguments)) + def test_methodcaller_cyclic_gc(self): + # gh-156762: Check for undefined behavior on calling methodcaller_clear() + operator = self.module + + class C: + pass + + c = C() + ref = weakref.ref(c) + c.m = operator.methodcaller('foo', c) + del c + + support.gc_collect() + self.assertIsNone(ref()) + def test_inplace(self): operator = self.module class C(object): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-43-24.gh-issue-156762.Kbf2mo.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-43-24.gh-issue-156762.Kbf2mo.rst new file mode 100644 index 000000000000000..f6f9bb038f4909d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-43-24.gh-issue-156762.Kbf2mo.rst @@ -0,0 +1,4 @@ +Fix undefined behaviour in :class:`operator.methodcaller`: its +:c:member:`~PyTypeObject.tp_clear` slot function returned ``void`` instead of +``int``, so the garbage collector called it through an incompatible function +type. Patched by Shamil Abdulaev. diff --git a/Modules/_operator.c b/Modules/_operator.c index 417403dc4c10c11..a0843971efe13e6 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -1740,7 +1740,7 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)mc; } -static void +static int methodcaller_clear(PyObject *op) { methodcallerobject *mc = methodcallerobject_CAST(op); @@ -1749,6 +1749,7 @@ methodcaller_clear(PyObject *op) Py_CLEAR(mc->kwds); Py_CLEAR(mc->vectorcall_args); Py_CLEAR(mc->vectorcall_kwnames); + return 0; } static void