diff options
author | Jan Vorlicek <janvorli@microsoft.com> | 2015-09-11 00:56:25 +0200 |
---|---|---|
committer | Jan Vorlicek <janvorli@microsoft.com> | 2015-09-11 00:56:25 +0200 |
commit | af76ec004c3d7c50abe29ddbfd3be37df30e21cb (patch) | |
tree | 10299677581d7c4207acf94535be0f3a2a7e3001 /src/pal/src/exception/seh-unwind.cpp | |
parent | b4c53d212ab86bdc1b9b948b22f88a0aea894fab (diff) | |
download | coreclr-af76ec004c3d7c50abe29ddbfd3be37df30e21cb.tar.gz coreclr-af76ec004c3d7c50abe29ddbfd3be37df30e21cb.tar.bz2 coreclr-af76ec004c3d7c50abe29ddbfd3be37df30e21cb.zip |
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')
-rw-r--r-- | src/pal/src/exception/seh-unwind.cpp | 29 |
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, #endif } -#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 } -#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; #endif + 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); + } + #if UNWIND_CONTEXT_IS_UCONTEXT_T WinContextToUnwindContext(context, &unwContext); #else @@ -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); |