From: Andrew Krieger Date: Fri, 23 Jun 2017 20:58:11 +0000 (-0700) Subject: Fix exception_wrapper on Windows by abusing internals X-Git-Tag: v2017.06.26.00~8 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=e7e6d29a00b685ab673fd76250ca3354088de46b Fix exception_wrapper on Windows by abusing internals Summary: exception_wrapper's non-allocating variant was depending on the exception reference being a reference to a heap object, which is not the case on Windows. All thrown exceptions are copied to the stack first. So, we abuse internals of exception_ptr to get the internally heap allocated copy of the object. What could possibly go wrong. Reviewed By: yfeldblum, ericniebler, Orvid Differential Revision: D5270634 fbshipit-source-id: 2983a6af9a2c3cb66cbee8a382707a76b582c489 --- diff --git a/folly/ExceptionWrapper-inl.h b/folly/ExceptionWrapper-inl.h index 287275ae..2845c2e3 100644 --- a/folly/ExceptionWrapper-inl.h +++ b/folly/ExceptionWrapper-inl.h @@ -18,6 +18,8 @@ * Author: Eric Niebler */ +#include + namespace folly { template @@ -90,11 +92,48 @@ inline std::exception const* exception_wrapper::as_exception_or_null_( return nullptr; } +static_assert( + !kIsWindows || sizeof(void*) == 8, + "exception_wrapper is untested on 32 bit Windows."); +static_assert( + !kIsWindows || (kMscVer >= 1900 && kMscVer <= 2000), + "exception_wrapper is untested and possibly broken on your version of " + "MSVC"); + inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_( + std::exception_ptr const& ptr, std::exception const& e) { - return reinterpret_cast(&e); + if (!kIsWindows) { + return reinterpret_cast(&e); + } else { + // On Windows, as of MSVC2017, all thrown exceptions are copied to the stack + // first. Thus, we cannot depend on exception references associated with an + // exception_ptr to be live for the duration of the exception_ptr. We need + // to directly access the heap allocated memory inside the exception_ptr. + // + // std::exception_ptr is an opaque reinterpret_cast of + // std::shared_ptr<__ExceptionPtr> + // __ExceptionPtr is a non-virtual class with two members, a union and a + // bool. The union contains the now-undocumented EHExceptionRecord, which + // contains a struct which contains a void* which points to the heap + // allocated exception. + // We derive the offset to pExceptionObject via manual means. + FOLLY_PACK_PUSH + struct Win32ExceptionPtr { + char offset[40]; + void* exceptionObject; + } FOLLY_PACK_ATTR; + FOLLY_PACK_POP + + auto* win32ExceptionPtr = + reinterpret_cast const*>( + &ptr)->get(); + return reinterpret_cast(win32ExceptionPtr->exceptionObject); + } } -inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(AnyException e) { +inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_( + std::exception_ptr const&, + AnyException e) { return reinterpret_cast(e.typeinfo_) + 1; } inline bool exception_wrapper::ExceptionPtr::has_exception_() const { @@ -282,7 +321,7 @@ inline exception_wrapper::~exception_wrapper() { template inline exception_wrapper::exception_wrapper(std::exception_ptr ptr, Ex& ex) - : eptr_{std::move(ptr), ExceptionPtr::as_int_(ex)}, + : eptr_{ptr, ExceptionPtr::as_int_(ptr, ex)}, vptr_(&ExceptionPtr::ops_) { assert(eptr_.ptr_); } diff --git a/folly/ExceptionWrapper.h b/folly/ExceptionWrapper.h index b00dc77d..449080d7 100644 --- a/folly/ExceptionWrapper.h +++ b/folly/ExceptionWrapper.h @@ -258,8 +258,12 @@ class exception_wrapper final { "Surprise! std::exception and std::type_info don't have alignment " "greater than one. as_int_ below will not work!"); - static std::uintptr_t as_int_(std::exception const& e); - static std::uintptr_t as_int_(AnyException e); + static std::uintptr_t as_int_( + std::exception_ptr const& ptr, + std::exception const& e); + static std::uintptr_t as_int_( + std::exception_ptr const& ptr, + AnyException e); bool has_exception_() const; std::exception const* as_exception_() const; std::type_info const* as_type_() const; diff --git a/folly/Portability.h b/folly/Portability.h index 7b822cda..7063e072 100644 --- a/folly/Portability.h +++ b/folly/Portability.h @@ -352,7 +352,9 @@ constexpr auto kIsLinux = false; #if defined(_WIN32) constexpr auto kIsWindows = true; +constexpr auto kMscVer = _MSC_VER; #else constexpr auto kIsWindows = false; +constexpr auto kMscVer = 0; #endif }