Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit f1d6f7a

Browse files
sbomerjkotas
authored andcommitted
Port from tfs: onload exception debugger crash fix (#4868)
The ExceptionHijackPersonalityRoutine was retrieving a context from a particular stack frame, but the calling conventions for the function executing in that frame allow it to overwrite the context. This was causing the debugger to crash for exceptions thrown from the onload method in winforms on x64. The fix is to instead retrieve the context from the previous stack frame, whose layout is set up explicitly in ExceptionHijack.
1 parent cc70bf8 commit f1d6f7a

File tree

3 files changed

+35
-1
lines changed

3 files changed

+35
-1
lines changed

src/debug/ee/amd64/dbghelpers.S

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ NESTED_ENTRY ExceptionHijack, _TEXT, UnhandledExceptionHandlerUnix
4545
// So we allocate 4 stack slots as the outgoing argument home and just copy the
4646
// arguments set up by DacDbi into these stack slots. We will take a perf hit,
4747
// but this is not a perf critical code path anyway.
48+
49+
// There is an additional dependency on this alloc_stack: the
50+
// ExceptionHijackPersonalityRoutine assumes that it can find
51+
// the first argument to HijackWorker in the stack frame of
52+
// ExceptionHijack, at an offset of exactly 0x20 bytes from
53+
// ExceptionHijackWorker's stack frame. Therefore it is
54+
// important that we move the stack pointer by the same amount.
4855
alloc_stack 0x20
4956
END_PROLOGUE
5057

@@ -53,6 +60,11 @@ NESTED_ENTRY ExceptionHijack, _TEXT, UnhandledExceptionHandlerUnix
5360
// stack for us. However, the Orcas compilers don't like a 0-sized frame, so
5461
// we need to allocate something here and then just copy the stack arguments to
5562
// their new argument homes.
63+
64+
// In x86, ExceptionHijackWorker is marked STDCALL, so it finds
65+
// its arguments on the stack. In x64, it gets its arguments in
66+
// registers (set up for us by DacDbiInterfaceImpl::Hijack),
67+
// and this stack space may be reused.
5668
mov rax, [rsp + 20h]
5769
mov [rsp], rax
5870
mov rax, [rsp + 28h]

src/debug/ee/amd64/dbghelpers.asm

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ NESTED_ENTRY ExceptionHijack, _TEXT, ExceptionHijackPersonalityRoutine
4848
; So we allocate 4 stack slots as the outgoing argument home and just copy the
4949
; arguments set up by DacDbi into these stack slots. We will take a perf hit,
5050
; but this is not a perf critical code path anyway.
51+
52+
; There is an additional dependency on this alloc_stack: the
53+
; ExceptionHijackPersonalityRoutine assumes that it can find
54+
; the first argument to HijackWorker in the stack frame of
55+
; ExceptionHijack, at an offset of exactly 0x20 bytes from
56+
; ExceptionHijackWorker's stack frame. Therefore it is
57+
; important that we move the stack pointer by the same amount.
5158
alloc_stack 20h
5259
END_PROLOGUE
5360

@@ -56,6 +63,11 @@ NESTED_ENTRY ExceptionHijack, _TEXT, ExceptionHijackPersonalityRoutine
5663
; stack for us. However, the Orcas compilers don't like a 0-sized frame, so
5764
; we need to allocate something here and then just copy the stack arguments to
5865
; their new argument homes.
66+
67+
; In x86, ExceptionHijackWorker is marked STDCALL, so it finds
68+
; its arguments on the stack. In x64, it gets its arguments in
69+
; registers (set up for us by DacDbiInterfaceImpl::Hijack),
70+
; and this stack space may be reused.
5971
mov rax, [rsp + 20h]
6072
mov [rsp], rax
6173
mov rax, [rsp + 28h]

src/debug/ee/debugger.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13502,7 +13502,17 @@ ExceptionHijackPersonalityRoutine(IN PEXCEPTION_RECORD pExceptionRecord
1350213502
CONTEXT * pHijackContext = NULL;
1350313503

1350413504
// Get the 1st parameter (the Context) from hijack worker.
13505-
pHijackContext = *reinterpret_cast<CONTEXT **>(pDispatcherContext->EstablisherFrame);
13505+
// EstablisherFrame points to the stack slot 8 bytes above the
13506+
// return address to the ExceptionHijack. This would contain the
13507+
// parameters passed to ExceptionHijackWorker, which is marked
13508+
// STDCALL, but the x64 calling convention lets the
13509+
// ExceptionHijackWorker use that stack space, resulting in the
13510+
// context being overwritten. Instead, we get the context from the
13511+
// previous stack frame, which contains the arguments to
13512+
// ExceptionHijack, placed there by the debugger in
13513+
// DacDbiInterfaceImpl::Hijack. This works because ExceptionHijack
13514+
// allocates exactly 4 stack slots.
13515+
pHijackContext = *reinterpret_cast<CONTEXT **>(pDispatcherContext->EstablisherFrame + 0x20);
1350613516

1350713517
// This copies pHijackContext into pDispatcherContext, which the OS can then
1350813518
// use to walk the stack.

0 commit comments

Comments
 (0)