From 099d6e9c48e5a2a76c4bad375e33d58e0834e587 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 24 Nov 2016 18:52:31 +0100 Subject: [PATCH] Improved implementation of error_already_set::~error_already_set() C++ exceptions are destructed in the context of the code that catches them. At this point, the Python GIL may not be held, which could lead to crashes with the previous implementation. PyErr_Fetch and PyErr_Restore should always occur in pairs, which was not the case for the previous implementation. To clear the exception, the new approach uses PyErr_Restore && PyErr_Clear instead of simply decreasing the reference counts of the exception objects. --- include/pybind11/common.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/pybind11/common.h b/include/pybind11/common.h index e0fa39223..e965324ca 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -482,7 +482,16 @@ public: : std::runtime_error(e.what()), type(e.type), value(e.value), trace(e.trace) { e.type = e.value = e.trace = nullptr; } - ~error_already_set() { Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(trace); } + ~error_already_set() { + if (value) { + PyGILState_STATE state = PyGILState_Ensure(); + PyErr_Restore(type, value, trace); + PyErr_Clear(); + PyGILState_Release(state); + } + } + + error_already_set& operator=(const error_already_set &) = delete; /// Give the error back to Python void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; }