Skip to content

Commit b0be6b3

Browse files
authored
bpo-29587: _PyErr_ChainExceptions() checks exception (GH-19902)
_PyErr_ChainExceptions() now ensures that the first parameter is an exception type, as done by _PyErr_SetObject(). * The following function now check PyExceptionInstance_Check() in an assertion using a new _PyBaseExceptionObject_cast() helper function: * PyException_GetTraceback(), PyException_SetTraceback() * PyException_GetCause(), PyException_SetCause() * PyException_GetContext(), PyException_SetContext() * PyExceptionClass_Name() now checks PyExceptionClass_Check() with an assertion. * Remove XXX comment and add gi_exc_state variable to _gen_throw(). * Remove comment from test_generators
1 parent 4e30ed3 commit b0be6b3

File tree

4 files changed

+45
-24
lines changed

4 files changed

+45
-24
lines changed

Lib/test/test_generators.py

-3
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,6 @@ def g():
342342
try:
343343
yield
344344
except Exception:
345-
# Without the `gi_exc_state.exc_type != Py_None` in
346-
# _gen_throw(), this line was causing a crash ("Segmentation
347-
# fault (core dumped)") on e.g. Fedora 32.
348345
raise RuntimeError
349346

350347
gen = g()

Objects/exceptions.c

+25-11
Original file line numberDiff line numberDiff line change
@@ -304,22 +304,33 @@ static PyGetSetDef BaseException_getset[] = {
304304
};
305305

306306

307+
static inline PyBaseExceptionObject*
308+
_PyBaseExceptionObject_cast(PyObject *exc)
309+
{
310+
assert(PyExceptionInstance_Check(exc));
311+
return (PyBaseExceptionObject *)exc;
312+
}
313+
314+
307315
PyObject *
308-
PyException_GetTraceback(PyObject *self) {
309-
PyBaseExceptionObject *base_self = (PyBaseExceptionObject *)self;
316+
PyException_GetTraceback(PyObject *self)
317+
{
318+
PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self);
310319
Py_XINCREF(base_self->traceback);
311320
return base_self->traceback;
312321
}
313322

314323

315324
int
316-
PyException_SetTraceback(PyObject *self, PyObject *tb) {
317-
return BaseException_set_tb((PyBaseExceptionObject *)self, tb, NULL);
325+
PyException_SetTraceback(PyObject *self, PyObject *tb)
326+
{
327+
return BaseException_set_tb(_PyBaseExceptionObject_cast(self), tb, NULL);
318328
}
319329

320330
PyObject *
321-
PyException_GetCause(PyObject *self) {
322-
PyObject *cause = ((PyBaseExceptionObject *)self)->cause;
331+
PyException_GetCause(PyObject *self)
332+
{
333+
PyObject *cause = _PyBaseExceptionObject_cast(self)->cause;
323334
Py_XINCREF(cause);
324335
return cause;
325336
}
@@ -328,13 +339,15 @@ PyException_GetCause(PyObject *self) {
328339
void
329340
PyException_SetCause(PyObject *self, PyObject *cause)
330341
{
331-
((PyBaseExceptionObject *)self)->suppress_context = 1;
332-
Py_XSETREF(((PyBaseExceptionObject *)self)->cause, cause);
342+
PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self);
343+
base_self->suppress_context = 1;
344+
Py_XSETREF(base_self->cause, cause);
333345
}
334346

335347
PyObject *
336-
PyException_GetContext(PyObject *self) {
337-
PyObject *context = ((PyBaseExceptionObject *)self)->context;
348+
PyException_GetContext(PyObject *self)
349+
{
350+
PyObject *context = _PyBaseExceptionObject_cast(self)->context;
338351
Py_XINCREF(context);
339352
return context;
340353
}
@@ -343,14 +356,15 @@ PyException_GetContext(PyObject *self) {
343356
void
344357
PyException_SetContext(PyObject *self, PyObject *context)
345358
{
346-
Py_XSETREF(((PyBaseExceptionObject *)self)->context, context);
359+
Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, context);
347360
}
348361

349362
#undef PyExceptionClass_Name
350363

351364
const char *
352365
PyExceptionClass_Name(PyObject *ob)
353366
{
367+
assert(PyExceptionClass_Check(ob));
354368
return ((PyTypeObject*)ob)->tp_name;
355369
}
356370

Objects/genobject.c

+9-9
Original file line numberDiff line numberDiff line change
@@ -512,15 +512,15 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
512512
}
513513

514514
PyErr_Restore(typ, val, tb);
515-
/* XXX It seems like we shouldn't have to check not equal to Py_None
516-
here because exc_type should only ever be a class. But not including
517-
this check was causing crashes on certain tests e.g. on Fedora. */
518-
if (gen->gi_exc_state.exc_type && gen->gi_exc_state.exc_type != Py_None) {
519-
Py_INCREF(gen->gi_exc_state.exc_type);
520-
Py_XINCREF(gen->gi_exc_state.exc_value);
521-
Py_XINCREF(gen->gi_exc_state.exc_traceback);
522-
_PyErr_ChainExceptions(gen->gi_exc_state.exc_type,
523-
gen->gi_exc_state.exc_value, gen->gi_exc_state.exc_traceback);
515+
516+
_PyErr_StackItem *gi_exc_state = &gen->gi_exc_state;
517+
if (gi_exc_state->exc_type != NULL && gi_exc_state->exc_type != Py_None) {
518+
Py_INCREF(gi_exc_state->exc_type);
519+
Py_XINCREF(gi_exc_state->exc_value);
520+
Py_XINCREF(gi_exc_state->exc_traceback);
521+
_PyErr_ChainExceptions(gi_exc_state->exc_type,
522+
gi_exc_state->exc_value,
523+
gi_exc_state->exc_traceback);
524524
}
525525
return gen_send_ex(gen, Py_None, 1, 0);
526526

Python/errors.c

+11-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
107107
if (exception != NULL &&
108108
!PyExceptionClass_Check(exception)) {
109109
_PyErr_Format(tstate, PyExc_SystemError,
110-
"exception %R not a BaseException subclass",
110+
"_PyErr_SetObject: "
111+
"exception %R is not a BaseException subclass",
111112
exception);
112113
return;
113114
}
@@ -484,6 +485,15 @@ _PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb)
484485
return;
485486

486487
PyThreadState *tstate = _PyThreadState_GET();
488+
489+
if (!PyExceptionClass_Check(exc)) {
490+
_PyErr_Format(tstate, PyExc_SystemError,
491+
"_PyErr_ChainExceptions: "
492+
"exception %R is not a BaseException subclass",
493+
exc);
494+
return;
495+
}
496+
487497
if (_PyErr_Occurred(tstate)) {
488498
PyObject *exc2, *val2, *tb2;
489499
_PyErr_Fetch(tstate, &exc2, &val2, &tb2);

0 commit comments

Comments
 (0)