From 87edf95599a0175394d1e233b995fad4dbe89288 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Fri, 29 Oct 2021 11:42:01 -0500 Subject: [PATCH] Save and restore the SEH state from the main greenlet into new greenlets. If we just grab it the first time we're switched to, we get things from the callers stack, which will unwind when it gets switched back to, leading to a corrupt SEH chain. --- src/greenlet/greenlet.cpp | 6 +++++- src/greenlet/greenlet_thread_state.hpp | 18 ++++++++++++++++++ src/greenlet/platform/switch_x86_msvc.h | 16 +++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/greenlet/greenlet.cpp b/src/greenlet/greenlet.cpp index b40b8b53..78cfd0e4 100644 --- a/src/greenlet/greenlet.cpp +++ b/src/greenlet/greenlet.cpp @@ -13,9 +13,10 @@ #include "greenlet_internal.hpp" #include "greenlet_refs.hpp" +#include "greenlet_slp_switch.hpp" #include "greenlet_thread_state.hpp" #include "greenlet_thread_support.hpp" -#include "greenlet_slp_switch.hpp" + using std::swap; using std::cerr; using std::endl; @@ -1258,7 +1259,10 @@ class SwitchingState { */ if (err.status == 1) { /* in the new greenlet */ + // TODO: Move this to its own 'noexcept' function: + // C++ exceptions cannot propagate to the parent greenlet from here. assert(this->thread_state.borrow_current() == this->target); + this->thread_state.restore_exception_state(); /* stack variables from above are no good and also will not unwind! */ // EXCEPT: That can't be true, we access run, among others, here. diff --git a/src/greenlet/greenlet_thread_state.hpp b/src/greenlet/greenlet_thread_state.hpp index 17be9919..924b8dc9 100644 --- a/src/greenlet/greenlet_thread_state.hpp +++ b/src/greenlet/greenlet_thread_state.hpp @@ -109,6 +109,9 @@ class ThreadState { them. */ greenlet::g_deleteme_t deleteme; +#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED + void* exception_state; +#endif static ImmortalObject get_referrers_name; static PythonAllocator allocator; @@ -150,6 +153,21 @@ class ThreadState { if (!get_referrers_name) { ThreadState::get_referrers_name = Greenlet_Intern("get_referrers"); } +#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED + this->exception_state = slp_get_exception_state(); +#endif + } + + inline void restore_exception_state() + { +#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED + // temp debug + fprintf(stderr, "About to restore exception state in new greenlet. Current chain:\n"); + slp_show_seh_chain(); + slp_set_exception_state(this->exception_state); + fprintf(stderr, "Did restore exception state in new greenlet. Current chain:\n"); + slp_show_seh_chain(); +#endif } inline bool has_main_greenlet() diff --git a/src/greenlet/platform/switch_x86_msvc.h b/src/greenlet/platform/switch_x86_msvc.h index 6a16cfc8..087be907 100644 --- a/src/greenlet/platform/switch_x86_msvc.h +++ b/src/greenlet/platform/switch_x86_msvc.h @@ -79,7 +79,21 @@ * * Help would be appreciated. */ -#define GREENLET_CANNOT_USE_EXCEPTIONS_NEAR_SWITCH 1 +//#define GREENLET_CANNOT_USE_EXCEPTIONS_NEAR_SWITCH 1 + +#define GREENLET_NEEDS_EXCEPTION_STATE_SAVED + +static void* +slp_get_exception_state() +{ + return (void*)__readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); +} + +static void +slp_set_exception_state(const void *const seh_state) +{ + __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state); +} typedef struct _GExceptionRegistration { struct _GExceptionRegistration* prev;