From c7f803b08ed5211701c75f98ba9ada85d45ac155 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Fri, 31 May 2019 03:46:36 -0600 Subject: [PATCH] bpo-36379: __ipow__ must be a ternaryfunc, not a binaryfunc (GH-13546) If a type's __ipow__ method was implemented in C, attempting to use the *modulo* parameter would cause crashes. https://bugs.python.org/issue36379 --- Lib/test/test_capi.py | 7 +++++ .../2019-05-24-07-11-08.bpo-36379.8zgoKe.rst | 2 ++ Modules/_testcapimodule.c | 26 +++++++++++++++++++ Objects/typeobject.c | 2 +- 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C API/2019-05-24-07-11-08.bpo-36379.8zgoKe.rst diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 795aa78d88..4dd78bb9a2 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -184,6 +184,13 @@ class CAPITest(unittest.TestCase): o @= m1 self.assertEqual(o, ("matmul", 42, m1)) + def test_c_type_with_ipow(self): + # When the __ipow__ method of a type was implemented in C, using the + # modulo param would cause segfaults. + o = _testcapi.ipowType() + self.assertEqual(o.__ipow__(1), (1, None)) + self.assertEqual(o.__ipow__(2, 2), (2, 2)) + def test_return_null_without_error(self): # Issue #23571: A function must not return NULL without setting an # error diff --git a/Misc/NEWS.d/next/C API/2019-05-24-07-11-08.bpo-36379.8zgoKe.rst b/Misc/NEWS.d/next/C API/2019-05-24-07-11-08.bpo-36379.8zgoKe.rst new file mode 100644 index 0000000000..6a699b2084 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2019-05-24-07-11-08.bpo-36379.8zgoKe.rst @@ -0,0 +1,2 @@ +Fix crashes when attempting to use the *modulo* parameter when ``__ipow__`` +is implemented in C. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index ca6e87b79c..b42f41cc8d 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5522,6 +5522,27 @@ static PyTypeObject matmulType = { PyObject_Del, /* tp_free */ }; +typedef struct { + PyObject_HEAD +} ipowObject; + +static PyObject * +ipowType_ipow(PyObject *self, PyObject *other, PyObject *mod) +{ + return Py_BuildValue("OO", other, mod); +} + +static PyNumberMethods ipowType_as_number = { + .nb_inplace_power = ipowType_ipow +}; + +static PyTypeObject ipowType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "ipowType", + .tp_basicsize = sizeof(ipowObject), + .tp_as_number = &ipowType_as_number, + .tp_new = PyType_GenericNew +}; typedef struct { PyObject_HEAD @@ -5947,6 +5968,11 @@ PyInit__testcapi(void) return NULL; Py_INCREF(&matmulType); PyModule_AddObject(m, "matmulType", (PyObject *)&matmulType); + if (PyType_Ready(&ipowType) < 0) { + return NULL; + } + Py_INCREF(&ipowType); + PyModule_AddObject(m, "ipowType", (PyObject *)&ipowType); if (PyType_Ready(&awaitType) < 0) return NULL; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 64c2ceab55..b6d925c144 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -7016,7 +7016,7 @@ static slotdef slotdefs[] = { IBSLOT("__imod__", nb_inplace_remainder, slot_nb_inplace_remainder, wrap_binaryfunc, "%="), IBSLOT("__ipow__", nb_inplace_power, slot_nb_inplace_power, - wrap_binaryfunc, "**="), + wrap_ternaryfunc, "**="), IBSLOT("__ilshift__", nb_inplace_lshift, slot_nb_inplace_lshift, wrap_binaryfunc, "<<="), IBSLOT("__irshift__", nb_inplace_rshift, slot_nb_inplace_rshift, -- 2.40.0