From 084f80b82c564c8a3cef26fc6e56da188a379be2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 27 Sep 2017 09:21:33 -0700 Subject: [PATCH] [3.6] bpo-31588: Validate return value of __prepare__() methods (GH-3790) Class execution requires that __prepare__() methods return a proper execution namespace. Check for that immediately after calling __prepare__(), rather than passing it through to the code execution machinery and potentially triggering SystemError (in debug builds) or a cryptic TypeError (in release builds). Patch by Oren Milman. (cherry picked from commit 5837d0418f47933b2e3c139bdee8a79c248a943c) --- Lib/test/test_types.py | 22 +++++++++++++++++++ .../2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst | 2 ++ Python/bltinmodule.c | 7 ++++++ 3 files changed, 31 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 382ca03e5a..d79429faf1 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -846,6 +846,28 @@ class ClassCreationTests(unittest.TestCase): self.assertIs(ns, expected_ns) self.assertEqual(len(kwds), 0) + def test_bad___prepare__(self): + # __prepare__() must return a mapping. + class BadMeta(type): + @classmethod + def __prepare__(*args): + return None + with self.assertRaisesRegex(TypeError, + r'^BadMeta\.__prepare__\(\) must ' + r'return a mapping, not NoneType$'): + class Foo(metaclass=BadMeta): + pass + # Also test the case in which the metaclass is not a type. + class BadMeta: + @classmethod + def __prepare__(*args): + return None + with self.assertRaisesRegex(TypeError, + r'^\.__prepare__\(\) must ' + r'return a mapping, not NoneType$'): + class Bar(metaclass=BadMeta()): + pass + def test_metaclass_derivation(self): # issue1294232: correct metaclass calculation new_calls = [] # to check the order of __new__ calls diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst b/Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst new file mode 100644 index 0000000000..44a7aba9d7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst @@ -0,0 +1,2 @@ +Raise a `TypeError` with a helpful error message when class creation fails +due to a metaclass with a bad ``__prepare__()`` method. Patch by Oren Milman. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 911e2ba79e..1960a095b8 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -167,6 +167,13 @@ builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) Py_DECREF(bases); return NULL; } + if (!PyMapping_Check(ns)) { + PyErr_Format(PyExc_TypeError, + "%.200s.__prepare__() must return a mapping, not %.200s", + isclass ? ((PyTypeObject *)meta)->tp_name : "", + Py_TYPE(ns)->tp_name); + goto error; + } cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns, NULL, 0, NULL, 0, NULL, 0, NULL, PyFunction_GET_CLOSURE(func)); -- 2.40.0