path: root/src/pal/src/exception/seh-unwind.cpp
diff options
authorJan Vorlicek <>2015-09-11 00:56:25 +0200
committerJan Vorlicek <>2015-09-11 00:56:25 +0200
commitaf76ec004c3d7c50abe29ddbfd3be37df30e21cb (patch)
tree10299677581d7c4207acf94535be0f3a2a7e3001 /src/pal/src/exception/seh-unwind.cpp
parentb4c53d212ab86bdc1b9b948b22f88a0aea894fab (diff)
Fix null reference exception handling in JIT_WriteBarrier
This change fixes an issue when a null reference exception happens in the first instruction of the JIT_WriteBarrier. There were two problems. First problem was that the native unwinder didn't know that it is unwinding a frame where the PC is an address of a failing instruction instead of the next address after a call instruction. So it decremented the PC before looking up the unwind info. Unfortunately, that means that if the hardware exception happens in the first instruction, the unwind info is not found and the unwinder resorts to RBP chain unwinding, which effectively skips one managed frame. The second problem was that the FaultingExceptionFrame we create when handling the hardware exception had context pointing to the JIT_WriteBarrier. But that breaks the stack walker. When it arrives at the FaultingExceptionFrame, it calls its ReturnAddress method and expects to get a managed code address. However, in this case, it was getting the address of the JIT_WriteBarrier instead and that made the stack walker to skip to the next explicit frame, effectively skipping multiple managed frames that it should have reported.
Diffstat (limited to 'src/pal/src/exception/seh-unwind.cpp')
1 files changed, 25 insertions, 4 deletions
diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp
index 23a05b410d..465c459dda 100644
--- a/src/pal/src/exception/seh-unwind.cpp
+++ b/src/pal/src/exception/seh-unwind.cpp
@@ -199,8 +199,6 @@ static void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext,
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_ARM64_)
static DWORD64 GetPc(CONTEXT *context)
#if defined(_AMD64_)
@@ -223,8 +221,6 @@ static void SetPc(CONTEXT *context, DWORD64 pc)
-#endif // defined(__APPLE__) || defined(__FreeBSD__) || defined(_ARM64_)
BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
int st;
@@ -234,6 +230,18 @@ BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextP
DWORD64 curPc;
+ if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
+ {
+ // The current frame is a source of hardware exception. Due to the fact that
+ // we use the low level unwinder to unwind just one frame a time, the
+ // unwinder doesn't have the signal_frame flag set. So it doesn't
+ // know that it should not decrement the PC before looking up the unwind info.
+ // So we compensate it by incrementing the PC before passing it to the unwinder.
+ // Without it, the unwinder would not find unwind info if the hardware exception
+ // happened in the first instruction of a function.
+ SetPc(context, GetPc(context) + 1);
+ }
WinContextToUnwindContext(context, &unwContext);
@@ -273,6 +281,19 @@ BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextP
return FALSE;
+ // Check if the frame we have unwound to is a frame that caused
+ // synchronous signal, like a hardware exception and record it
+ // in the context flags.
+ st = unw_is_signal_frame(&cursor);
+ if (st > 0)
+ {
+ context->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+ }
+ else
+ {
+ context->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE;
+ }
// Update the passed in windows context to reflect the unwind
UnwindContextToWinContext(&cursor, context);