Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 6 additions & 22 deletions src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,6 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHan
return hr;
}

bool g_fSkipStackCheck = false;
bool g_fSkipStackCheckInit = false;

// Check whether the specified CONTEXT is valid. The only check we perform right now is whether the
// SP in the specified CONTEXT is in the stack range of the thread.
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CheckContext(VMPTR_Thread vmThread,
Expand All @@ -355,27 +352,14 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CheckContext(VMPTR_Thread v
return S_OK;
}

if (!g_fSkipStackCheckInit)
{
g_fSkipStackCheck = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DbgSkipStackCheck) != 0);
g_fSkipStackCheckInit = true;
}
// We don't have the backing store boundaries stored on the thread, but this is just
// a sanity check anyway.
Thread * pThread = vmThread.GetDacPtr();
PTR_VOID sp = GetSP(reinterpret_cast<const T_CONTEXT *>(pContext));

// Skip this check if the customer has set the reg key/env var. This is necessary for AutoCad. They
// enable fiber mode by calling the Win32 API ConvertThreadToFiber(), but when a managed debugger is
// attached, they don't actually call into our hosting APIs such as SwitchInLogicalThreadState(). This
// leads to the cached stack range on the Thread object being stale.
if (!g_fSkipStackCheck)
if ((sp < pThread->GetCachedStackLimit()) || (pThread->GetCachedStackBase() <= sp))
{
// We don't have the backing store boundaries stored on the thread, but this is just
// a sanity check anyway.
Thread * pThread = vmThread.GetDacPtr();
PTR_VOID sp = GetSP(reinterpret_cast<const T_CONTEXT *>(pContext));

if ((sp < pThread->GetCachedStackLimit()) || (pThread->GetCachedStackBase() <= sp))
{
return CORDBG_E_NON_MATCHING_CONTEXT;
}
return CORDBG_E_NON_MATCHING_CONTEXT;
}

return S_OK;
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ CONFIG_DWORD_INFO(INTERNAL_EnCResolveField, W("EnCResolveField"), 0, "Allows bre
CONFIG_DWORD_INFO(INTERNAL_EncResumeInUpdatedFunction, W("EncResumeInUpdatedFunction"), 0, "Allows breaking when execution resumes in a new EnC version of a function")
CONFIG_DWORD_INFO(INTERNAL_DbgAssertOnDebuggeeDebugBreak, W("DbgAssertOnDebuggeeDebugBreak"), 0, "If non-zero causes the managed-only debugger to assert on unhandled breakpoints in the debuggee")
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException, W("UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException"), 0, "If non-zero, then don't try to unsuspend threads after continuing a 2nd-chance native exception")
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgSkipStackCheck, W("DbgSkipStackCheck"), 0, "Skip the stack pointer check during stackwalking")
#ifdef DACCESS_COMPILE
CONFIG_DWORD_INFO(INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget, W("IntentionallyCorruptDataFromTarget"), 0, "Intentionally fakes bad data retrieved from target to try and break dump generation.")
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ public static class CorDbgHResults
public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303);
public const int CORDBG_E_TARGET_INCONSISTENT = unchecked((int)0x80131c36);
public const int CORDBG_S_NOT_ALL_BITS_SET = unchecked((int)0x00131c13);
public const int CORDBG_E_NON_MATCHING_CONTEXT = unchecked((int)0x80131327);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public enum ContextFlagsValues : uint
}

public readonly uint Size => 0x4d0;

public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL;

public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public enum ContextFlagsValues : uint

public readonly uint Size => 0x390;

public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL;

public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public enum ContextFlagsValues : uint
}

public readonly uint Size => 0x1a0;

public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL;

public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public sealed class ContextHolder<T> : IPlatformAgnosticContext, IEquatable<Cont
public T Context;

public uint Size => Context.Size;
public uint ContextControlFlags => Context.ContextControlFlags;
public uint FullContextFlags => Context.FullContextFlags;
public uint AllContextFlags => Context.AllContextFlags;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
public interface IPlatformAgnosticContext
{
public abstract uint Size { get; }
public abstract uint ContextControlFlags { get; }
public abstract uint FullContextFlags { get; }
public abstract uint AllContextFlags { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
public interface IPlatformContext
{
uint Size { get; }
uint ContextControlFlags { get; }
uint FullContextFlags { get; }
uint AllContextFlags { get; }
Comment thread
rcj1 marked this conversation as resolved.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public enum ContextFlagsValues : uint

public readonly uint Size => 0x320;

public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL;

public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public enum ContextFlagsValues : uint

public readonly uint Size => 0x220;

public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL;

public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public enum ContextFlagsValues : uint
}

public readonly uint Size => 0x2cc;

public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL;

public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,36 @@ public int UnwindStackWalkFrame(nuint pSFIHandle, Interop.BOOL* pResult)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.UnwindStackWalkFrame(pSFIHandle, pResult) : HResults.E_NOTIMPL;

public int CheckContext(ulong vmThread, nint pContext)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.CheckContext(vmThread, pContext) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target);
ctx.FillFromBuffer(new Span<byte>((void*)pContext, (int)ctx.Size));

if ((ctx.RawContextFlags & ctx.ContextControlFlags) != 0)
{
_target.Contracts.Thread.GetStackLimitData(new TargetPointer(vmThread), out TargetPointer stackBase, out TargetPointer stackLimit, out _);
TargetPointer sp = ctx.StackPointer;
if (sp < stackLimit || stackBase <= sp)
{
hr = CorDbgHResults.CORDBG_E_NON_MATCHING_CONTEXT;
}
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
int hrLocal = _legacy.CheckContext(vmThread, pContext);
Debug.ValidateHResult(hr, hrLocal);
}
#endif
return hr;
}

public int GetStackWalkCurrentFrameInfo(nuint pSFIHandle, nint pFrameData, int* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetStackWalkCurrentFrameInfo(pSFIHandle, pFrameData, pRetVal) : HResults.E_NOTIMPL;
Expand Down
95 changes: 95 additions & 0 deletions src/native/managed/cdac/tests/DacDbiImplTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Moq;
using Xunit;
Expand Down Expand Up @@ -462,4 +463,98 @@ public void GetSymbolsBuffer_EmptyStream(MockTarget.Architecture arch)
Assert.Equal(0u, targetBuffer.cbSize);
Assert.Equal(SymbolFormat.None, symbolFormat);
}

public static IEnumerable<object[]> TargetArchitectures()
{
string[] architectures = ["x64", "arm64", "arm", "x86", "loongarch64", "riscv64"];
foreach (object[] stdArch in new MockTarget.StdArch())
{
foreach (string archName in architectures)
{
yield return [stdArch[0], archName];
}
}
}

public static IEnumerable<object[]> TargetArchitectures_SpRange()
{
foreach (object[] archData in TargetArchitectures())
{
yield return [archData[0], archData[1], (ulong)0x6000, System.HResults.S_OK];
yield return [archData[0], archData[1], (ulong)0x2000, CorDbgHResults.CORDBG_E_NON_MATCHING_CONTEXT];
yield return [archData[0], archData[1], (ulong)0x8000, CorDbgHResults.CORDBG_E_NON_MATCHING_CONTEXT];
}
}

[Theory]
[MemberData(nameof(TargetArchitectures_SpRange))]
public void CheckContext_WithControlFlag_ValidatesSpRange(MockTarget.Architecture arch, string targetArch, ulong sp, int expectedHr)
{
const ulong ThreadAddr = 0x1000;
var (dacDbi, target) = CreateCheckContextDacDbi(arch, targetArch, ThreadAddr, stackBase: 0x8000, stackLimit: 0x4000);

IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(target);
ctx.RawContextFlags = ctx.ContextControlFlags;
ctx.StackPointer = new TargetPointer(sp);
byte[] bytes = ctx.GetBytes();

fixed (byte* pCtx = bytes)
{
int hr = dacDbi.CheckContext(ThreadAddr, (nint)pCtx);
Assert.Equal(expectedHr, hr);
}
}

[Theory]
[MemberData(nameof(TargetArchitectures))]
public void CheckContext_NoControlFlag_SkipsSpCheck(MockTarget.Architecture arch, string targetArch)
{
const ulong ThreadAddr = 0x1000;
var mockThread = new Mock<IThread>();

var target = new TestPlaceholderTarget.Builder(arch)
.AddGlobalStrings((Constants.Globals.Architecture, targetArch))
.AddContract<IRuntimeInfo>(version: "c1")
.AddMockContract(mockThread)
.Build();
var dacDbi = new DacDbiImpl(target, legacyObj: null);

IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(target);
ctx.RawContextFlags = 0;
ctx.StackPointer = new TargetPointer(0x2000);
byte[] bytes = ctx.GetBytes();

fixed (byte* pCtx = bytes)
{
int hr = dacDbi.CheckContext(ThreadAddr, (nint)pCtx);
Assert.Equal(System.HResults.S_OK, hr);
}

mockThread.Verify(
t => t.GetStackLimitData(It.IsAny<TargetPointer>(), out It.Ref<TargetPointer>.IsAny, out It.Ref<TargetPointer>.IsAny, out It.Ref<TargetPointer>.IsAny),
Times.Never);
}

private static (DacDbiImpl DacDbi, Target Target) CreateCheckContextDacDbi(MockTarget.Architecture arch, string targetArch, ulong threadAddr, ulong stackBase, ulong stackLimit)
{
var mockThread = new Mock<IThread>();
mockThread
.Setup(t => t.GetStackLimitData(new TargetPointer(threadAddr), out It.Ref<TargetPointer>.IsAny, out It.Ref<TargetPointer>.IsAny, out It.Ref<TargetPointer>.IsAny))
.Callback(new GetStackLimitDataCallback((TargetPointer _, out TargetPointer sb, out TargetPointer sl, out TargetPointer fa) =>
{
sb = new TargetPointer(stackBase);
sl = new TargetPointer(stackLimit);
fa = TargetPointer.Null;
}));

var target = new TestPlaceholderTarget.Builder(arch)
.AddGlobalStrings((Constants.Globals.Architecture, targetArch))
.AddContract<IRuntimeInfo>(version: "c1")
.AddMockContract(mockThread)
.Build();

return (new DacDbiImpl(target, legacyObj: null), target);
}

private delegate void GetStackLimitDataCallback(TargetPointer threadPointer, out TargetPointer stackBase, out TargetPointer stackLimit, out TargetPointer frameAddress);
}