Skip to content

Commit 83ca46b

Browse files
closes bpo-39091: Fix segfault when Exception constructor returns non-exception for gen.throw. (python#17658)
Co-authored-by: Benjamin Peterson <benjamin@python.org>
1 parent 54f185b commit 83ca46b

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

Lib/test/test_generators.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,32 @@ def gen():
270270
self.assertEqual(next(g), "done")
271271
self.assertEqual(sys.exc_info(), (None, None, None))
272272

273+
def test_except_throw_bad_exception(self):
274+
class E(Exception):
275+
def __new__(cls, *args, **kwargs):
276+
return cls
277+
278+
def boring_generator():
279+
yield
280+
281+
gen = boring_generator()
282+
283+
err_msg = 'should have returned an instance of BaseException'
284+
285+
with self.assertRaisesRegex(TypeError, err_msg):
286+
gen.throw(E)
287+
288+
self.assertRaises(StopIteration, next, gen)
289+
290+
def generator():
291+
with self.assertRaisesRegex(TypeError, err_msg):
292+
yield
293+
294+
gen = generator()
295+
next(gen)
296+
with self.assertRaises(StopIteration):
297+
gen.throw(E)
298+
273299
def test_stopiteration_error(self):
274300
# See also PEP 479.
275301

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,7 @@ Peter Otten
13001300
Michael Otteneder
13011301
Richard Oudkerk
13021302
Russel Owen
1303+
Noah Oxer
13031304
Joonas Paalasmaa
13041305
Yaroslav Pankovych
13051306
Martin Packman
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash when using passing a non-exception to a generator's ``throw()`` method. Patch by Noah Oxer

Python/errors.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,29 @@ _PyErr_GetTopmostException(PyThreadState *tstate)
8585
}
8686

8787
static PyObject*
88-
_PyErr_CreateException(PyObject *exception, PyObject *value)
88+
_PyErr_CreateException(PyObject *exception_type, PyObject *value)
8989
{
90+
PyObject *exc;
91+
9092
if (value == NULL || value == Py_None) {
91-
return _PyObject_CallNoArg(exception);
93+
exc = _PyObject_CallNoArg(exception_type);
9294
}
9395
else if (PyTuple_Check(value)) {
94-
return PyObject_Call(exception, value, NULL);
96+
exc = PyObject_Call(exception_type, value, NULL);
9597
}
9698
else {
97-
return PyObject_CallOneArg(exception, value);
99+
exc = PyObject_CallOneArg(exception_type, value);
100+
}
101+
102+
if (exc != NULL && !PyExceptionInstance_Check(exc)) {
103+
PyErr_Format(PyExc_TypeError,
104+
"calling %R should have returned an instance of "
105+
"BaseException, not %s",
106+
exception_type, Py_TYPE(exc)->tp_name);
107+
Py_CLEAR(exc);
98108
}
109+
110+
return exc;
99111
}
100112

101113
void

0 commit comments

Comments
 (0)