From fd669773b69edac5bf0bdd7ab27b709be7dc0777 Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Tue, 13 Jun 2023 20:41:31 -0400 Subject: [PATCH] Use guarded pages for coroutine stack on windows --- include/tool/coroutine.h | 59 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/include/tool/coroutine.h b/include/tool/coroutine.h index fe086ef413..b40c2d9812 100644 --- a/include/tool/coroutine.h +++ b/include/tool/coroutine.h @@ -49,6 +49,11 @@ #include #include +#ifdef _WIN32 +#include +#include +#endif + /** * Implement a coroutine. * @@ -186,7 +191,10 @@ public: /** * Create a coroutine from a delegate object. */ - COROUTINE( std::function aEntry ) : + COROUTINE( std::function aEntry ) : +#ifdef _WIN32 + m_stack( nullptr ), +#endif m_func( std::move( aEntry ) ), m_running( false ), m_args( nullptr ), @@ -215,6 +223,11 @@ public: if( m_callee.ctx ) libcontext::release_fcontext( m_callee.ctx ); + +#ifdef _WIN32 + if( m_stack ) + ::VirtualFree( m_stack, 0, MEM_RELEASE ); +#endif } public: @@ -375,7 +388,46 @@ private: void* sp = nullptr; #ifndef LIBCONTEXT_HAS_OWN_STACK +#ifdef _WIN32 + // kind of a hack to avoid calling into GetSystemInfo constantly for page size + static std::optional system_page_size; + if( !system_page_size.has_value() ) + { + // For Windows, we want to use VirtualAlloc and VirtualProtect which let us + // trap stack overflows with the guard page, it'll still crash but this + // has a chance of preserving stack in dumps + SYSTEM_INFO si; + ::GetSystemInfo( &si ); + system_page_size = static_cast( si.dwPageSize ); + } + + // this shouldn't happen but just incase + if( m_stack ) + ::VirtualFree( m_stack, 0, MEM_RELEASE ); + + // calculate the correct number of pages to allocate based on request stack size + std::size_t pages = ( m_stacksize + system_page_size.value() - 1 ) / system_page_size.value(); + + // we allocate an extra page for the guard + std::size_t alloc_size = ( pages + 1 ) * system_page_size.value(); + + m_stack = ::VirtualAlloc( 0, alloc_size, MEM_COMMIT, PAGE_READWRITE ); + if( !m_stack ) + throw std::bad_alloc(); + + // now configure the first page (by only specifying a single page_size from vp) + // that will act as the guard page + // the stack will grow from the end and hopefully never into this guarded region + DWORD old_prot; // dummy var since the arg cannot be NULL + const BOOL res = ::VirtualProtect( m_stack, system_page_size.value(), + PAGE_READWRITE | PAGE_GUARD, &old_prot ); + + assert( res != false ); + + stackSize = alloc_size; + sp = static_cast( m_stack ) + stackSize; +#else // fixme: Clean up stack stuff. Add a guard m_stack.reset( new char[stackSize] ); @@ -389,6 +441,7 @@ private: m_valgrind_stack = VALGRIND_STACK_REGISTER( sp, m_stack.get() ); #endif #endif +#endif #ifdef KICAD_SANITIZE_THREADS // Create a new fiber to go with the new context @@ -475,7 +528,11 @@ private: } ///< coroutine stack +#ifdef _WIN32 + void* m_stack; +#else std::unique_ptr m_stack; +#endif int m_stacksize;