Skip to content

Commit f9016e3

Browse files
authored
[NativeAOT] When reconciling shadow stack after catch, use more precise way to figure how much to pop. (#104652)
* keep SSP value in REGDISPLAY, update as we unwind. * fix Unix build * make SSP fields windows-specific * #if defined(TARGET_WINDOWS) some uses of SSP
1 parent 9565550 commit f9016e3

File tree

7 files changed

+99
-22
lines changed

7 files changed

+99
-22
lines changed

src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,11 @@ struct PAL_LIMITED_CONTEXT
153153
uintptr_t R13;
154154
uintptr_t R14;
155155
uintptr_t R15;
156+
#if defined(TARGET_WINDOWS)
157+
uintptr_t SSP;
158+
#else
156159
uintptr_t __explicit_padding__;
160+
#endif // TARGET_WINDOWS
157161
Fp128 Xmm6;
158162
Fp128 Xmm7;
159163
Fp128 Xmm8;

src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,14 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO
516516
m_RegDisplay.pR13 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R13);
517517
m_RegDisplay.pR14 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R14);
518518
m_RegDisplay.pR15 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R15);
519+
520+
#if defined(TARGET_WINDOWS)
521+
//
522+
// SSP, we only need the value
523+
//
524+
m_RegDisplay.SSP = pCtx->SSP;
525+
#endif
526+
519527
//
520528
// preserved xmm regs
521529
//
@@ -632,6 +640,13 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC
632640
m_RegDisplay.pR14 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R14);
633641
m_RegDisplay.pR15 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R15);
634642

643+
#if defined(TARGET_WINDOWS)
644+
//
645+
// SSP, not needed. Unwind from native context is never for EH.
646+
//
647+
m_RegDisplay.SSP = 0;
648+
#endif
649+
635650
//
636651
// scratch regs
637652
//
@@ -1004,6 +1019,13 @@ void StackFrameIterator::UnwindFuncletInvokeThunk()
10041019
m_RegDisplay.pR14 = SP++;
10051020
m_RegDisplay.pR15 = SP++;
10061021

1022+
#if defined(TARGET_WINDOWS)
1023+
if (m_RegDisplay.SSP)
1024+
{
1025+
m_RegDisplay.SSP += 8;
1026+
}
1027+
#endif
1028+
10071029
#elif defined(TARGET_X86)
10081030
SP = (PTR_uintptr_t)(m_RegDisplay.SP + 0x4); // skip the saved assembly-routine-EBP
10091031

@@ -1368,6 +1390,12 @@ void StackFrameIterator::UnwindUniversalTransitionThunk()
13681390
m_RegDisplay.SetIP(PCODEToPINSTR(*addressOfPushedCallerIP));
13691391
m_RegDisplay.SetSP((uintptr_t)dac_cast<TADDR>(stackFrame->get_CallerSP()));
13701392
SetControlPC(dac_cast<PTR_VOID>(m_RegDisplay.GetIP()));
1393+
#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
1394+
if (m_RegDisplay.SSP)
1395+
{
1396+
m_RegDisplay.SSP += 8;
1397+
}
1398+
#endif
13711399

13721400
// All universal transition cases rely on conservative GC reporting being applied to the
13731401
// full argument set that flowed into the call. Report the lower bound of this range (the
@@ -1428,6 +1456,14 @@ void StackFrameIterator::UnwindThrowSiteThunk()
14281456
m_RegDisplay.pR13 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R13);
14291457
m_RegDisplay.pR14 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R14);
14301458
m_RegDisplay.pR15 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R15);
1459+
1460+
#if defined(TARGET_WINDOWS)
1461+
if (m_RegDisplay.SSP)
1462+
{
1463+
m_RegDisplay.SSP += 8;
1464+
}
1465+
#endif
1466+
14311467
#elif defined(TARGET_ARM)
14321468
m_RegDisplay.pR4 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R4);
14331469
m_RegDisplay.pR5 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R5);

src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ PLAT_ASM_OFFSET(0f0, PAL_LIMITED_CONTEXT, Xmm15)
5959
PLAT_ASM_SIZEOF(130, REGDISPLAY)
6060
PLAT_ASM_OFFSET(78, REGDISPLAY, SP)
6161
PLAT_ASM_OFFSET(80, REGDISPLAY, IP)
62+
PLAT_ASM_OFFSET(88, REGDISPLAY, SSP)
6263

6364
PLAT_ASM_OFFSET(18, REGDISPLAY, pRbx)
6465
PLAT_ASM_OFFSET(20, REGDISPLAY, pRbp)

src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ ALTERNATE_ENTRY RhpThrowHwExGEHCONT ; this needs to be an EHCONT target since we
4444
.pushframe
4545

4646
alloc_stack SIZEOF_XmmSaves + 8h ;; reserve stack for the xmm saves (+8h to realign stack)
47-
push_vol_reg r8 ;; padding
47+
rdsspq r8 ;; nop if SSP is not implemented, 0 if not enabled
48+
push_vol_reg r8 ;; SSP
49+
xor r8, r8
4850
push_nonvol_reg r15
4951
push_nonvol_reg r14
5052
push_nonvol_reg r13
@@ -127,7 +129,9 @@ NESTED_ENTRY RhpThrowEx, _TEXT
127129
xor r8, r8
128130

129131
alloc_stack SIZEOF_XmmSaves + 8h ;; reserve stack for the xmm saves (+8h to realign stack)
130-
push_vol_reg r8 ;; padding
132+
rdsspq r8 ;; nop if SSP is not implemented, 0 if not enabled
133+
push_vol_reg r8 ;; SSP
134+
xor r8, r8
131135
push_nonvol_reg r15
132136
push_nonvol_reg r14
133137
push_nonvol_reg r13
@@ -221,7 +225,9 @@ NESTED_ENTRY RhpRethrow, _TEXT
221225
xor r8, r8
222226

223227
alloc_stack SIZEOF_XmmSaves + 8h ;; reserve stack for the xmm saves (+8h to realign stack)
224-
push_vol_reg r8 ;; padding
228+
rdsspq r8 ;; nop if SSP is not implemented, 0 if not enabled
229+
push_vol_reg r8 ;; SSP
230+
xor r8, r8
225231
push_nonvol_reg r15
226232
push_nonvol_reg r14
227233
push_nonvol_reg r13
@@ -490,7 +496,7 @@ endif
490496
INLINE_THREAD_UNHIJACK rdx, rcx, r9 ;; Thread in rdx, trashes rcx and r9
491497

492498
mov rcx, [rsp + rsp_offsetof_arguments + 18h] ;; rcx <- current ExInfo *
493-
mov r10, [r8 + OFFSETOF__REGDISPLAY__IP] ;; r10 <- original IP value
499+
mov r11, [r8 + OFFSETOF__REGDISPLAY__SSP] ;; r11 <- resume SSP value
494500
mov r8, [r8 + OFFSETOF__REGDISPLAY__SP] ;; r8 <- resume SP value
495501
xor r9, r9 ;; r9 <- 0
496502

@@ -505,7 +511,7 @@ endif
505511
;; Sanity check: if we have shadow stack, it should agree with what we have in rsp
506512
LOCAL_STACK_USE equ 118h
507513
ifdef _DEBUG
508-
rdsspq r9
514+
rdsspq r9 ;; NB, r9 == 0 prior to this
509515
test r9, r9
510516
jz @f
511517
mov r9, [r9]
@@ -531,23 +537,23 @@ endif
531537
;; reset RSP and jump to RAX
532538
@@: mov rsp, r8 ;; reset the SP to resume SP value
533539

534-
;; if have shadow stack, then we need to reconcile it with the rsp change we have just made
535-
rdsspq r9
540+
;; if have shadow stack, then we need to reconcile it with the rsp change we have just made. (r11 must contain target SSP)
541+
rdsspq r9 ;; NB, r9 == 0 prior to this
536542
test r9, r9
537-
jz NoSSP
538-
539-
;; Find the shadow stack pointer for the frame we are going to restore to.
540-
;; The SSP we search is pointing to the return address of the frame represented
541-
;; by the passed in context. So we search for the instruction pointer from
542-
;; the context and return one slot up from there.
543-
;; (Same logic as in GetSSPForFrameOnCurrentStack)
544-
xor r11, r11
545-
@@: inc r11
546-
cmp [r9 + r11 * 8 - 8], r10
547-
jne @b
548-
549-
incsspq r11
550-
NoSSP: jmp rax
543+
je No_Ssp_Update
544+
sub r11, r9
545+
shr r11, 3
546+
;; the incsspq instruction uses only the lowest 8 bits of the argument, so we need to loop in case the increment is larger than 255
547+
mov r9, 255
548+
Update_Loop:
549+
cmp r11, r9
550+
cmovb r9, r11
551+
incsspq r9
552+
sub r11, r9
553+
ja Update_Loop
554+
555+
556+
No_Ssp_Update: jmp rax
551557

552558

553559
NESTED_END RhpCallCatchFunclet, _TEXT

src/coreclr/nativeaot/Runtime/regdisplay.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ struct REGDISPLAY
3030
#endif // TARGET_AMD64
3131

3232
uintptr_t SP;
33-
PCODE IP;
33+
PCODE IP;
34+
35+
#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
36+
uintptr_t SSP; // keep track of SSP for EH unwind
37+
// we do not adjust the original, so only need the value
38+
#endif // TARGET_AMD64 && TARGET_WINDOWS
3439

3540
#if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
3641
Fp128 Xmm[16-6]; // preserved xmm6..xmm15 regs for EH stackwalk
@@ -68,6 +73,7 @@ struct REGDISPLAY
6873
inline void SetEdiLocation(unsigned long *loc) { pRdi = (PTR_uintptr_t)loc; }
6974
inline void SetEbpLocation(unsigned long *loc) { pRbp = (PTR_uintptr_t)loc; }
7075
#endif
76+
7177
};
7278

7379
#ifdef TARGET_X86

src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,10 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo,
790790
if (!(flags & USFF_GcUnwind))
791791
{
792792
memcpy(pRegisterSet->Xmm, &context.Xmm6, sizeof(pRegisterSet->Xmm));
793+
if (pRegisterSet->SSP)
794+
{
795+
pRegisterSet->SSP += 8;
796+
}
793797
}
794798
#elif defined(TARGET_ARM64)
795799
if (!(flags & USFF_GcUnwind))

src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,10 +2356,30 @@ public void Blagh()
23562356
}
23572357
}
23582358

2359+
[MethodImpl(MethodImplOptions.NoInlining)]
2360+
private static void DeepAV(int x)
2361+
{
2362+
if (x > 0)
2363+
DeepAV(x - 1);
2364+
2365+
// Call an instance method on something we never allocated, but overrides a used virtual.
2366+
// This asserted the compiler when trying to build a template for Unused<__Canon>.
2367+
((Unused<object>)s_ref).Blagh();
2368+
}
2369+
23592370
public static void Run()
23602371
{
23612372
new Used().DoStuff();
23622373

2374+
for (int i = 0; i < 10; i++)
2375+
try
2376+
{
2377+
DeepAV(i);
2378+
}
2379+
catch (NullReferenceException)
2380+
{
2381+
}
2382+
23632383
try
23642384
{
23652385
// Call an instance method on something we never allocated, but overrides a used virtual.

0 commit comments

Comments
 (0)