Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -52,5 +53,35 @@ internal static void ValidateHResult(
};
Debug.Assert(match, $"HResult mismatch - cDAC: 0x{unchecked((uint)cdacHr):X8}, DAC: 0x{unchecked((uint)dacHr):X8} ({Path.GetFileName(filePath)}:{lineNumber})");
}

[Conditional("DEBUG")]
internal static unsafe void ValidateOutputStringBuffer(
char* cdacBuffer,
uint* cdacNeeded,
char[] dacBuffer,
uint dacNeeded,
uint count,
[CallerFilePath] string? filePath = null,
[CallerLineNumber] int lineNumber = 0)
{
string location = $"{Path.GetFileName(filePath)}:{lineNumber}";

if (cdacNeeded is not null && *cdacNeeded != dacNeeded)
{
int cdacLen = (int)Math.Min(*cdacNeeded, count);
string cdacStr = cdacBuffer is not null && cdacLen > 0 ? new string(cdacBuffer, 0, cdacLen - 1) : "<null>";
string dacStr = dacNeeded > 0 ? new string(dacBuffer, 0, (int)dacNeeded - 1) : "<empty>";
Comment on lines +72 to +73
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValidateOutputStringBuffer can throw while trying to build the diagnostic dacStr when dacNeeded is greater than the provided dacBuffer length (often count). In the mismatch case, dacNeeded is exactly the scenario where the caller’s buffer may be too small, so new string(dacBuffer, 0, (int)dacNeeded - 1) risks ArgumentOutOfRangeException and defeats the purpose of soft-fail diagnostics. Clamp the length to the available buffer size (e.g., Math.Min(dacNeeded, (uint)dacBuffer.Length) / Math.Min(dacNeeded, count)) before constructing the string.

Suggested change
string cdacStr = cdacBuffer is not null && cdacLen > 0 ? new string(cdacBuffer, 0, cdacLen - 1) : "<null>";
string dacStr = dacNeeded > 0 ? new string(dacBuffer, 0, (int)dacNeeded - 1) : "<empty>";
int dacLen = (int)Math.Min(dacNeeded, (uint)dacBuffer.Length);
string cdacStr = cdacBuffer is not null && cdacLen > 0 ? new string(cdacBuffer, 0, cdacLen - 1) : "<null>";
string dacStr = dacLen > 0 ? new string(dacBuffer, 0, dacLen - 1) : "<empty>";

Copilot uses AI. Check for mistakes.
Trace.TraceWarning($"Output buffer pNeeded mismatch ({location}) - cDAC: {*cdacNeeded} (\"{cdacStr}\"), DAC: {dacNeeded} (\"{dacStr}\")");
}
else if (cdacBuffer is not null && dacNeeded > 0 && count >= dacNeeded)
{
var cdacSpan = new ReadOnlySpan<char>(cdacBuffer, (int)dacNeeded - 1);
var dacSpan = new ReadOnlySpan<char>(dacBuffer, 0, (int)dacNeeded - 1);
if (!cdacSpan.SequenceEqual(dacSpan))
{
Trace.TraceWarning($"Output buffer content mismatch ({location}) - cDAC: \"{new string(cdacBuffer, 0, (int)dacNeeded - 1)}\", DAC: \"{new string(dacBuffer, 0, (int)dacNeeded - 1)}\"");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2995,6 +2995,13 @@ int ISOSDacInterface.GetMethodTableName(ClrDataAddress mt, uint count, char* mtN
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
if (pNeeded != null && *pNeeded != neededLocal)
{
string cdacStr = mtName is not null && count > 0 ? new string(mtName, 0, (int)System.Math.Min(*pNeeded, count) - 1) : "<null>";
string dacStr = neededLocal > 0 ? new string(mtNameLocal, 0, (int)neededLocal - 1) : "<empty>";
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diagnostic dacStr construction can throw when neededLocal is larger than count (e.g., buffer was too small and legacy DAC reported a bigger required size). Since mtNameLocal is allocated as new char[count], new string(mtNameLocal, 0, (int)neededLocal - 1) will go out of range and may prevent the mismatch details from being logged. Consider clamping the extracted length to the available buffer size (e.g., Math.Min(neededLocal, count)), similar to what’s done for cdacStr.

Suggested change
string dacStr = neededLocal > 0 ? new string(mtNameLocal, 0, (int)neededLocal - 1) : "<empty>";
uint dacCount = System.Math.Min(neededLocal, count);
string dacStr = dacCount > 0 ? new string(mtNameLocal, 0, (int)dacCount - 1) : "<empty>";

Copilot uses AI. Check for mistakes.
System.Console.Error.WriteLine($"CDAC_MISMATCH GetMethodTableName MT=0x{(ulong)mt:X} pNeeded: cDAC={*pNeeded} DAC={neededLocal} cDAC_name=\"{cdacStr}\" DAC_name=\"{dacStr}\"");
System.Console.Error.Flush();
}
Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
Debug.Assert(mtName == null || new ReadOnlySpan<char>(mtNameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(mtName)));
}
Expand Down
Loading