Fix exception in PreStubWorker
authorJan Vorlicek <janvorli@microsoft.com>
Tue, 26 Jan 2016 01:48:37 +0000 (02:48 +0100)
committerJan Vorlicek <janvorli@microsoft.com>
Tue, 26 Jan 2016 10:38:47 +0000 (11:38 +0100)
This change fixes a problem when exception happens in managed code called from
the PreStubWorker and the PreStubWorker (resp. its caller, ThePreStub) was called
from native code.
The issue was that the INSTALL_MANAGED_EXCEPTION_DISPATCHER calls DispatchManagedException
and that function expected that the INSTALL_MANAGED_EXCEPTION_DISPATCHER was always
at the boundary between managed and native frames and so it skipped the native frames
upto the first managed frame.
The PreStubWorker is the only case where this is not always true and so the native frames
need to be unwound by rethrowing the C++ exception when the PreStubWorker was called from
native code.

src/pal/inc/pal.h
src/vm/callhelpers.h
src/vm/exceptionhandling.cpp

index 56963673db087dbe94185c3f8284cb4a65834b77..4e408ebbf79f82c086bb8aa14218e95a628e5067 100644 (file)
@@ -6896,6 +6896,22 @@ public:
     }
 };
 
+// This is a native exception holder that doesn't catch any exceptions.
+class NativeExceptionHolderNoCatch : public NativeExceptionHolderBase
+{
+
+public:
+    NativeExceptionHolderNoCatch()
+        : NativeExceptionHolderBase()
+    {
+    }
+
+    virtual EXCEPTION_DISPOSITION InvokeFilter(PAL_SEHException& ex)
+    {
+        return EXCEPTION_CONTINUE_SEARCH;
+    }
+};
+
 //
 // This factory class for the native exception holder is necessary because
 // templated functions don't need the explicit type parameter and can infer
index 497835f6d5d28180ba86442693a5dccdad162fcb..a3fa41b58a93cf83a0bbf3553f79558f0e71c4f7 100644 (file)
@@ -447,6 +447,20 @@ void FillInRegTypeMap(int argOffset, CorElementType typ, BYTE * pMap);
 /* Macros used to indicate a call to managed code is starting/ending   */
 /***********************************************************************/
 
+#ifdef FEATURE_PAL
+// Install a native exception holder that doesn't catch any exceptions but its presence
+// in a stack range of native frames indicates that there was a call from native to
+// managed code. It is used by the DispatchManagedException to detect the case when
+// the INSTALL_MANAGED_EXCEPTION_DISPATCHER was not at the managed to native boundary.
+// For example in the PreStubWorker, which can be called from both native and managed
+// code.
+#define INSTALL_CALL_TO_MANAGED_EXCEPTION_HOLDER() \
+    NativeExceptionHolderNoCatch __exceptionHolder;    \
+    __exceptionHolder.Push();                           
+#else // FEATURE_PAL
+#define INSTALL_CALL_TO_MANAGED_EXCEPTION_HOLDER()
+#endif // FEATURE_PAL
+
 enum EEToManagedCallFlags
 {
     EEToManagedDefault                  = 0x0000,
@@ -478,6 +492,7 @@ enum EEToManagedCallFlags
         }                                                                       \
     }                                                                           \
     BEGIN_SO_TOLERANT_CODE(CURRENT_THREAD);                                     \
+    INSTALL_CALL_TO_MANAGED_EXCEPTION_HOLDER();                                 \
     INSTALL_COMPLUS_EXCEPTION_HANDLER_NO_DECLARE();
 
 #define END_CALL_TO_MANAGED()                                                   \
index cbb7bc23102c86a11e15bf8145d57d1c92776ff4..c45db6de3860871d9202e978f602956716b2ecb0 100644 (file)
@@ -4671,8 +4671,18 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex)
                 // Get the managed frame to continue unwinding from.
                 CONTEXT frameContext;
                 RtlCaptureContext(&frameContext);
+                UINT_PTR currentSP = GetSP(&frameContext);
                 Thread::VirtualUnwindToFirstManagedCallFrame(&frameContext);
+                UINT_PTR firstManagedFrameSP = GetSP(&frameContext);
+            
+                // Check if there is any exception holder in the skipped frames. If there is one, we need to unwind them
+                // using the C++ handling. This is a special case when the UNINSTALL_MANAGED_EXCEPTION_DISPATCHER was
+                // not at the managed to native boundary.
+                if (NativeExceptionHolderBase::FindNextHolder(nullptr, (void*)currentSP, (void*)firstManagedFrameSP) != nullptr)
+                {
+                    break;
+                }
+
                 UnwindManagedExceptionPass2(ex, &frameContext);
             }
             UNREACHABLE();
@@ -4681,8 +4691,11 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex)
         {
             ex = ex2;
         }
+
     }
     while (true);
+
+    throw ex;
 }
 
 #ifdef _AMD64_