-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix SSP restoring in edge cases #104820
Fix SSP restoring in edge cases #104820
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,13 +108,20 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats | |
if (updateFloats) | ||
{ | ||
UpdateFloatingPointRegisters(pRD); | ||
// The float updating unwinds the stack so the pRD->pCurrentContext->Rip contains correct unwound Rip | ||
// This is used for exception handling and the Rip extracted from m_pCallerReturnAddress is slightly | ||
// off, which causes problem with searching for the return address on shadow stack on x64, so | ||
// we keep the value from the unwind. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was it a subtle bug that the Rip was off, or we just did not care about the correct IP in this case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's actually expected due to the way how the InlinedCallFrame is created in some cases. Neither the GC stack walk nor the new EH cared, but it caused problem with the newly added search for the first managed frame, as it sometimes is one that called the pinvoke (QCALL). |
||
} | ||
else | ||
#endif // DACCESS_COMPILE | ||
{ | ||
pRD->pCurrentContext->Rip = *(DWORD64 *)&m_pCallerReturnAddress; | ||
} | ||
|
||
pRD->IsCallerContextValid = FALSE; | ||
pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. | ||
|
||
pRD->pCurrentContext->Rip = *(DWORD64 *)&m_pCallerReturnAddress; | ||
pRD->pCurrentContext->Rsp = *(DWORD64 *)&m_pCallSiteSP; | ||
pRD->pCurrentContext->Rbp = *(DWORD64 *)&m_pCalleeSavedFP; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
using System; | ||
using System.Runtime.CompilerServices; | ||
using Xunit; | ||
|
||
public class Test104820 | ||
{ | ||
// Test that SSP is updated properly when resuming after catch when the shadow stack | ||
// contains the instruction pointer of the resume frame multiple times and the SSP needs | ||
// to be restored to the location of one that's not the first one found on the shadow | ||
// stack. | ||
// This test fails with stack overflow of the shadow stack if the SSP is updated incorrectly. | ||
static void ShadowStackPointerUpdateTest(int depth) | ||
{ | ||
if (depth == 0) | ||
{ | ||
throw new Exception(); | ||
} | ||
|
||
for (int i = 0; i < 1000; i++) | ||
{ | ||
try | ||
{ | ||
try | ||
{ | ||
ShadowStackPointerUpdateTest(depth - 1); | ||
} | ||
catch (Exception) | ||
{ | ||
throw new Exception(); | ||
} | ||
} | ||
catch (Exception) when (depth == 100) | ||
{ | ||
} | ||
} | ||
} | ||
|
||
[Fact] | ||
public static void TestEntryPoint() | ||
{ | ||
ShadowStackPointerUpdateTest(100); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<CLRTestPriority>1</CLRTestPriority> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="test104820.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" /> | ||
</ItemGroup> | ||
</Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is indeed documented as only bits 0-7 are used, regardless of operand size.
Perhaps there is a way to save a byte of assembly by doing
INCSSPD eax
:-)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The incsspd increments the SSP by multiples of 4, not by 8 :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also make it use bits 0-3, because why not ... :-)