From 2fa9454c9ff0bef874363bfec4a2d543ead41bf0 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Fri, 13 Nov 2015 21:07:44 -0800 Subject: [PATCH] Python - Save and restore exception state before calling destroy. PyObject_CallFunction has the potential to silently drop the active exception. In cases where the user just finished iterating a generator, StopIteration will be active. Most of the time this is fine because destroy() won't raise an exception. On Python 3 however, and with a debug interpreter, you will get an assertion failure inside of Python. And in the worst case scenario, if destroy() does throw an exception, the intepreter probably won't be able to correctly detect the end of the iteration. --- Lib/python/pyrun.swg | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Lib/python/pyrun.swg b/Lib/python/pyrun.swg index 5eedca483..ba9d6ecca 100644 --- a/Lib/python/pyrun.swg +++ b/Lib/python/pyrun.swg @@ -537,14 +537,21 @@ SwigPyObject_dealloc(PyObject *v) /* destroy is always a VARARGS method */ PyObject *res; if (data->delargs) { - /* we need to create a temporary object to carry the destroy operation */ - PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); - res = SWIG_Python_CallFunctor(destroy, tmp); - Py_DECREF(tmp); + /* we need to create a temporary object to carry the destroy operation */ + PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); + /* PyObject_CallFunction() has the potential to silently drop the active + active exception. In cases where we just finished iterating over a + generator StopIteration will be active right now, and this needs to + remain true upon return from SwigPyObject_dealloc. So save and restore. */ + PyObject *val = NULL, *type = NULL, *tb = NULL; + PyErr_Fetch(&val, &type, &tb); + res = SWIG_Python_CallFunctor(destroy, tmp); + PyErr_Restore(val, type, tb); + Py_DECREF(tmp); } else { - PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); - PyObject *mself = PyCFunction_GET_SELF(destroy); - res = ((*meth)(mself, v)); + PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); + PyObject *mself = PyCFunction_GET_SELF(destroy); + res = ((*meth)(mself, v)); } Py_XDECREF(res); }