From 83ca46b7784b7357d82ec47b33295e09ed7380cb Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Mon, 2 Aug 2021 21:17:18 -0500 Subject: [PATCH] closes bpo-39091: Fix segfault when Exception constructor returns non-exception for gen.throw. (#17658) Co-authored-by: Benjamin Peterson --- Lib/test/test_generators.py | 26 +++++++++++++++++++ Misc/ACKS | 1 + .../2019-12-21-14-18-32.bpo-39091.dOexgQ.rst | 1 + Python/errors.c | 20 +++++++++++--- 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-12-21-14-18-32.bpo-39091.dOexgQ.rst diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index ebf8bb7e320..53d579e723c 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -270,6 +270,32 @@ class ExceptionTest(unittest.TestCase): self.assertEqual(next(g), "done") self.assertEqual(sys.exc_info(), (None, None, None)) + def test_except_throw_bad_exception(self): + class E(Exception): + def __new__(cls, *args, **kwargs): + return cls + + def boring_generator(): + yield + + gen = boring_generator() + + err_msg = 'should have returned an instance of BaseException' + + with self.assertRaisesRegex(TypeError, err_msg): + gen.throw(E) + + self.assertRaises(StopIteration, next, gen) + + def generator(): + with self.assertRaisesRegex(TypeError, err_msg): + yield + + gen = generator() + next(gen) + with self.assertRaises(StopIteration): + gen.throw(E) + def test_stopiteration_error(self): # See also PEP 479. diff --git a/Misc/ACKS b/Misc/ACKS index 97a360c8ecf..f845c1c5a48 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1300,6 +1300,7 @@ Peter Otten Michael Otteneder Richard Oudkerk Russel Owen +Noah Oxer Joonas Paalasmaa Yaroslav Pankovych Martin Packman diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-12-21-14-18-32.bpo-39091.dOexgQ.rst b/Misc/NEWS.d/next/Core and Builtins/2019-12-21-14-18-32.bpo-39091.dOexgQ.rst new file mode 100644 index 00000000000..c3b4e810d65 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-12-21-14-18-32.bpo-39091.dOexgQ.rst @@ -0,0 +1 @@ +Fix crash when using passing a non-exception to a generator's ``throw()`` method. Patch by Noah Oxer diff --git a/Python/errors.c b/Python/errors.c index 1f84215a136..eeb84e83835 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -85,17 +85,29 @@ _PyErr_GetTopmostException(PyThreadState *tstate) } static PyObject* -_PyErr_CreateException(PyObject *exception, PyObject *value) +_PyErr_CreateException(PyObject *exception_type, PyObject *value) { + PyObject *exc; + if (value == NULL || value == Py_None) { - return _PyObject_CallNoArg(exception); + exc = _PyObject_CallNoArg(exception_type); } else if (PyTuple_Check(value)) { - return PyObject_Call(exception, value, NULL); + exc = PyObject_Call(exception_type, value, NULL); } else { - return PyObject_CallOneArg(exception, value); + exc = PyObject_CallOneArg(exception_type, value); } + + if (exc != NULL && !PyExceptionInstance_Check(exc)) { + PyErr_Format(PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %s", + exception_type, Py_TYPE(exc)->tp_name); + Py_CLEAR(exc); + } + + return exc; } void