Skip to content

Implement DnsResolver for Linux - #2

Open
anurag6569201 wants to merge 1 commit into
qa/agent-dotnet-runtime/pr-02-129846/basefrom
qa/agent-dotnet-runtime/pr-02-129846/head
Open

anurag6569201 wants to merge 1 commit into
qa/agent-dotnet-runtime/pr-02-129846/basefrom
qa/agent-dotnet-runtime/pr-02-129846/head

Conversation

@anurag6569201

Copy link
Copy Markdown

Follow up on dotnet#129845

Source merge-base: 7600052d92b249ebda3210bd34ff345811faa408
Source head: 60c7a85bf7edaf52aa1d3a66dd834bd5f32d3c42

@shipwright-agent

Copy link
Copy Markdown

⛔ Shipwright · Blocked

Recommendation: do not merge PR #2 · Tier T3
Checks: 0 total · 0 needing attention

Next step: resolve the blocking findings before merge.

Findings (12)

  • CRITICAL DnsLabelEnumerator.MoveNext reads _buffer[_pos] without any bounds check. · src/libraries/System.Net.NameResolution/src/System/Net/DnsEncodedName.cs:527
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • CRITICAL DnsEncodedName.TryEncode computes wireLen = name.Length + 2 before validating that each label is <= 63 bytes. · src/libraries/System.Net.NameResolution/src/System/Net/DnsEncodedName.cs:145
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • CRITICAL DnsEncodedName.ValidateName allows compression pointers to jump backward to any offset, including into the middle of a label or into the header. · src/libraries/System.Net.NameResolution/src/System/Net/DnsEncodedName.cs:430
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • CRITICAL DnsEncodedName.TryEncode uses a static shared IdnMapping instance (s_idnMapping) without synchronization. · src/libraries/System.Net.NameResolution/src/System/Net/DnsEncodedName.cs:17
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • CRITICAL DnsEncodedName.TryDecodeAscii widens each byte directly to char ((char)label[i]) for response labels, explicitly allowing bytes outside ASCII range. · src/libraries/System.Net.NameResolution/src/System/Net/DnsEncodedName.cs:365
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH DnsEncodedName.TryEncode has a subtle bug: it writes the label length byte to destination[labelStart] BEFORE validating the label with IsValidLabel. · src/libraries/System.Net.NameResolution/src/System/Net/DnsEncodedName.cs:155
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH The DnsEncodedName struct is a ref struct with a private ReadOnlySpan<byte> _buffer field. · src/libraries/System.Net.NameResolution/src/System/Net/DnsEncodedName.cs:14
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH The test project compiles production source files directly (Compile Include with Link) rather than referencing the production assembly. · src/libraries/System.Net.NameResolution/tests/UnitTests/System.Net.NameResolution.Unit.Tests.csproj:14
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • …and 4 more findings in the check details.

Fireworks usage: 51,047 input · 1,649 output · 52,696 total tokens · $0.0123 · 24s · 0 fix iteration(s)

Open the Shipwright check for full evidence and the audit bundle. Use /shipwright rerun to verify again.

private static readonly SearchValues<byte> s_ldhBytes =
SearchValues.Create("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"u8);

// Validates that a label has valid length (1-63), contains only LDH (Letters,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

DnsLabelEnumerator.MoveNext reads _buffer[_pos] without any bounds check.

Impact: DnsLabelEnumerator.MoveNext reads _buffer[_pos] without any bounds check. The enumerator is public and can be constructed directly via DnsEncodedName.EnumerateLabels() on a default/empty DnsEncodedName, or after TryParse fails, yielding _pos=0 on an empty buffer. This throws IndexOutOfRangeException instead of returning false, violating the Try* contract and crashing on malformed input.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

int labelStart = 0;
bool isAce = aceName != null;
while (true)
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

DnsEncodedName.TryEncode computes wireLen = name.Length + 2 before validating that each label is <= 63 bytes.

Impact: DnsEncodedName.TryEncode computes wireLen = name.Length + 2 before validating that each label is <= 63 bytes. A name with a single label longer than 63 characters (e.g., 200 chars) passes the wireLen <= 255 check, then IsValidLabel rejects it. However, a name with multiple labels where one label is 64-253 bytes can have wireLen <= 255 but the label length byte written to destination[labelStart] truncates to…

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

// encoding), label content is also validated for LDH compliance.
private static bool ValidateName(ReadOnlySpan<byte> buffer, int offset,
out int wireLength, out int formattedLength, out bool isAce,
out bool hasPointers, bool validateContent = false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

DnsEncodedName.ValidateName allows compression pointers to jump backward to any offset, including into the middle of a label or into the header.

Impact: DnsEncodedName.ValidateName allows compression pointers to jump backward to any offset, including into the middle of a label or into the header. The pointer target is not validated to be a label boundary. An attacker can craft a response where a pointer targets a length byte that is actually part of RDATA or the header, causing the parser to interpret arbitrary bytes as label lengths and potentially read out of boun…

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

internal readonly ref struct DnsEncodedName
{
private static readonly IdnMapping s_idnMapping = new IdnMapping { AllowUnassigned = false, UseStd3AsciiRules = true };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

DnsEncodedName.TryEncode uses a static shared IdnMapping instance (s_idnMapping) without synchronization.

Impact: DnsEncodedName.TryEncode uses a static shared IdnMapping instance (s_idnMapping) without synchronization. IdnMapping.GetAscii/TryGetUnicode are not documented thread-safe, and this type is used from the DNS resolver which can be called concurrently from multiple threads. Concurrent use can corrupt internal state or produce incorrect ACE encodings, leading to wrong DNS queries or IDN spoofing.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

public override unsafe string ToString()
{
Span<char> chars = stackalloc char[MaxEncodedLength + 1];
bool success = TryDecode(chars, out int charsWritten);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

DnsEncodedName.TryDecodeAscii widens each byte directly to char ((char)label[i]) for response labels, explicitly allowing bytes outside ASCII range.

Impact: DnsEncodedName.TryDecodeAscii widens each byte directly to char ((char)label[i]) for response labels, explicitly allowing bytes outside ASCII range. This means attacker-controlled DNS responses can inject arbitrary Unicode characters into the decoded name, which is then used for hostname comparison, certificate validation, or displayed to users. This is a DNS response injection / IDN homograph attack vector.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

return OperationStatus.InvalidData;
}

if (!isAce)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

DnsEncodedName.TryEncode has a subtle bug: it writes the label length byte to destination[labelStart] BEFORE validating the label with IsValidLabel.

Impact: DnsEncodedName.TryEncode has a subtle bug: it writes the label length byte to destination[labelStart] BEFORE validating the label with IsValidLabel. If validation fails, the destination buffer is left in a partially modified state, and the caller may not realize the buffer was corrupted. The write should happen after validation.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

// Represents a domain name in DNS wire format (RFC 1035 §4.1.4).
// Works for both the read path (responses with compression pointers) and the
// write path (flat encoded names).
internal readonly ref struct DnsEncodedName

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

The DnsEncodedName struct is a ref struct with a private ReadOnlySpan<byte> _buffer field.

Impact: The DnsEncodedName struct is a ref struct with a private ReadOnlySpan<byte> _buffer field. This makes it impossible to store in heap objects or async state machines. The DNS resolver is inherently async (Dns.GetHostAddressesAsync), so this design forces all parsing to happen synchronously on the stack, which may not be compatible with the async resolver architecture.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

</ItemGroup>
<!-- Production code under test: the managed DNS wire-format parsing/encoding types. -->
<ItemGroup>
<Compile Include="..\..\src\System\Net\DnsEncodedName.cs"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

The test project compiles production source files directly (Compile Include with Link) rather than referencing the production assembly.

Impact: The test project compiles production source files directly (Compile Include with Link) rather than referencing the production assembly. This means tests exercise a copy of the code, not the actual shipped assembly. If the production csproj and test csproj diverge in compilation conditions or defines, tests can pass while production fails.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

// DnsHeaderFlags enum values are the wire bit positions shifted right by 4,
// so the enum fits in a byte. Encoding shifts left by 4 to restore wire positions,
// decoding shifts right by 4. The Z bit (wire bit 6) gap is preserved by the shift.
// Wire flag bits: AA(10) TC(9) RD(8) RA(7) AD(5) CD(4)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

DnsMessageHeader.EncodeFlagsWord uses FlagsShift=4 and WireFlagsMask=0x07F0, but the comment says the Z bit (wire bit 6) gap is preserved.

Impact: DnsMessageHeader.EncodeFlagsWord uses FlagsShift=4 and WireFlagsMask=0x07F0, but the comment says the Z bit (wire bit 6) gap is preserved. The mask 0x07F0 covers bits 4-10, which includes bit 6 (Z). If the Flags enum ever has a value in the Z bit position, it will be silently encoded. The mask should be 0x0770 to exclude bit 6.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

{
ascii[..asciiWritten].CopyTo(destination);
charsWritten = asciiWritten;
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

DnsEncodedName.GetFormattedLength and ToString use stackalloc char[MaxEncodedLength + 1] (256 chars) and Debug.Assert(success) on TryDecode.

Impact: DnsEncodedName.GetFormattedLength and ToString use stackalloc char[MaxEncodedLength + 1] (256 chars) and Debug.Assert(success) on TryDecode. For ACE names, the Unicode form can be longer than the ASCII wire form in some edge cases (e.g., certain IDN mappings), causing TryDecode to fail and the assert to fire in debug builds, or silent truncation in release builds.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'unix'">
<Compile Include="System\Net\NameResolutionPal.Unix.cs" />
<Compile Include="System\Net\DnsResolverPal.Unsupported.cs" />
<Compile Include="System\Net\DnsResolverPal.Managed.cs" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

The new managed DNS resolver (DnsResolverPal.Managed.cs, DnsSocket.cs, ResolvConf.cs) replaces the unsupported stub on Unix.

Impact: The new managed DNS resolver (DnsResolverPal.Managed.cs, DnsSocket.cs, ResolvConf.cs) replaces the unsupported stub on Unix. This is a significant new attack surface: it parses untrusted DNS responses from the network. The diff shows extensive parsing code but no evidence of response validation against the query (transaction ID, question section matching, source address validation). DNS spoofing/cache poisoning is a…

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

}
}

// Strip trailing dot from the comparison name.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

DnsEncodedName.Equals converts the comparison name to ACE using s_idnMapping.GetAscii but does not validate that the result is a valid DNS name.

Impact: DnsEncodedName.Equals converts the comparison name to ACE using s_idnMapping.GetAscii but does not validate that the result is a valid DNS name. An attacker-controlled comparison string with invalid IDN characters could throw or produce unexpected results, potentially bypassing name comparison checks.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant