Skip to content

Commit

Permalink
Save and restore the SEH state from the main greenlet into new greenl…
Browse files Browse the repository at this point in the history
…ets.

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.
  • Loading branch information
jamadden committed Oct 29, 2021
1 parent a289fcf commit 87edf95
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 2 deletions.
6 changes: 5 additions & 1 deletion src/greenlet/greenlet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.

Expand Down
18 changes: 18 additions & 0 deletions src/greenlet/greenlet_thread_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ThreadState> allocator;
Expand Down Expand Up @@ -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()
Expand Down
16 changes: 15 additions & 1 deletion src/greenlet/platform/switch_x86_msvc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 87edf95

Please sign in to comment.