From ebcb0575343e1767e89abc10fb68a2a6925c1b15 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 21 Apr 2025 07:32:19 -0400 Subject: [PATCH 1/5] Boy oh boy do I love null dereferences --- Objects/descrobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 268af0b217cd98..5ff36edd3ddb72 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -145,7 +145,7 @@ method_get(PyObject *self, PyObject *obj, PyObject *type) return NULL; } if (descr->d_method->ml_flags & METH_METHOD) { - if (PyType_Check(type)) { + if (type == NULL || PyType_Check(type)) { return PyCMethod_New(descr->d_method, obj, NULL, descr->d_common.d_type); } else { PyErr_Format(PyExc_TypeError, From b9b4fa22e6966ada1cdf72f8f7b5feb8575e5350 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 21 Apr 2025 07:37:27 -0400 Subject: [PATCH 2/5] Add simple test. --- Lib/test/test_types.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index e5d80ee8eb7aca..0954992b612f56 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -653,6 +653,24 @@ def test_method_descriptor_types(self): self.assertIsInstance(int.from_bytes, types.BuiltinMethodType) self.assertIsInstance(int.__new__, types.BuiltinMethodType) + def test_method_descriptor_crash(self): + # gh-132747: The default __get__() implementation in C was unable + # to handle a second argument of None when called from Python + import _io + import io + import _queue + + types = [ + # (method, instance) + (_io.TextIOWrapper.write, io.StringIO()), + (_queue.SimpleQueue.put, _queue.SimpleQueue()), + (str.capitalize, "nobody expects the spanish inquisition") + ] + + for method, instance in types: + with self.subTest(method=method, instance=instance): + method.__get__(instance) + def test_ellipsis_type(self): self.assertIsInstance(Ellipsis, types.EllipsisType) From ad71a506d1f6a670b1f74e06b9f61dd0f48ad27d Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 21 Apr 2025 07:38:24 -0400 Subject: [PATCH 3/5] Fix the test. --- Lib/test/test_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 0954992b612f56..1e36741caeb859 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -662,7 +662,7 @@ def test_method_descriptor_crash(self): types = [ # (method, instance) - (_io.TextIOWrapper.write, io.StringIO()), + (_io._TextIOBase.read, io.StringIO()), (_queue.SimpleQueue.put, _queue.SimpleQueue()), (str.capitalize, "nobody expects the spanish inquisition") ] From 9dff96376aa587dd2f04a217379859516b141e36 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 21 Apr 2025 07:40:06 -0400 Subject: [PATCH 4/5] Add blurb. --- .../2025-04-21-07-39-59.gh-issue-132747.L-cnej.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-21-07-39-59.gh-issue-132747.L-cnej.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-21-07-39-59.gh-issue-132747.L-cnej.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-21-07-39-59.gh-issue-132747.L-cnej.rst new file mode 100644 index 00000000000000..c6d45b09f64519 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-21-07-39-59.gh-issue-132747.L-cnej.rst @@ -0,0 +1,2 @@ +Fix a crash when calling :meth:`~object.__get__` of a :term:`method` with a +:const:`None` second argument. From c0563c29a41eb18aee07a094e3b218e5cea646d5 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 21 Apr 2025 12:17:53 +0000 Subject: [PATCH 5/5] Add an assertion. --- Lib/test/test_types.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 1e36741caeb859..3552b6b4ef846c 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -660,16 +660,17 @@ def test_method_descriptor_crash(self): import io import _queue - types = [ + to_check = [ # (method, instance) (_io._TextIOBase.read, io.StringIO()), (_queue.SimpleQueue.put, _queue.SimpleQueue()), (str.capitalize, "nobody expects the spanish inquisition") ] - for method, instance in types: + for method, instance in to_check: with self.subTest(method=method, instance=instance): - method.__get__(instance) + bound = method.__get__(instance) + self.assertIsInstance(bound, types.BuiltinMethodType) def test_ellipsis_type(self): self.assertIsInstance(Ellipsis, types.EllipsisType)