Skip to content

Commit 8c191eb

Browse files
committed
AssemblyNameInfo fuzzer (dotnet#107195)
* add initial AssemblyNameInfo Fuzzer * fix the first bug that it has discovered
1 parent dec716d commit 8c191eb

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed

eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ extends:
6565
# displayName: Send to OneFuzz
6666

6767
# ONEFUZZ_TASK_WORKAROUND_START
68+
- task: onefuzz-task@0
69+
inputs:
70+
onefuzzOSes: 'Windows'
71+
env:
72+
onefuzzDropDirectory: $(fuzzerProject)/deployment/AssemblyNameInfoFuzzer
73+
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
74+
displayName: Send AssemblyNameInfoFuzzer to OneFuzz
75+
6876
- task: onefuzz-task@0
6977
inputs:
7078
onefuzzOSes: 'Windows'
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Buffers;
5+
using System.Reflection.Metadata;
6+
using System.Runtime.InteropServices;
7+
8+
namespace DotnetFuzzing.Fuzzers
9+
{
10+
internal sealed class AssemblyNameInfoFuzzer : IFuzzer
11+
{
12+
public string[] TargetAssemblies => ["System.Reflection.Metadata"];
13+
14+
public string[] TargetCoreLibPrefixes => [];
15+
16+
public void FuzzTarget(ReadOnlySpan<byte> bytes)
17+
{
18+
ReadOnlySpan<char> chars = MemoryMarshal.Cast<byte, char>(bytes);
19+
20+
using PooledBoundedMemory<char> inputPoisonedBefore = PooledBoundedMemory<char>.Rent(chars, PoisonPagePlacement.Before);
21+
using PooledBoundedMemory<char> inputPoisonedAfter = PooledBoundedMemory<char>.Rent(chars, PoisonPagePlacement.After);
22+
23+
Test(inputPoisonedBefore);
24+
Test(inputPoisonedAfter);
25+
}
26+
27+
private static void Test(PooledBoundedMemory<char> inputPoisoned)
28+
{
29+
bool shouldSucceed = AssemblyNameInfo.TryParse(inputPoisoned.Span, out _);
30+
31+
try
32+
{
33+
AssemblyNameInfo.Parse(inputPoisoned.Span);
34+
}
35+
catch (ArgumentException)
36+
{
37+
Assert.Equal(false, shouldSucceed);
38+
return;
39+
}
40+
41+
Assert.Equal(true, shouldSucceed);
42+
}
43+
}
44+
}

src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/AssemblyNameInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public static AssemblyNameInfo Parse(ReadOnlySpan<char> assemblyName)
193193
public static bool TryParse(ReadOnlySpan<char> assemblyName, [NotNullWhen(true)] out AssemblyNameInfo? result)
194194
{
195195
AssemblyNameParser.AssemblyNameParts parts = default;
196-
if (AssemblyNameParser.TryParse(assemblyName, ref parts))
196+
if (!assemblyName.IsEmpty && AssemblyNameParser.TryParse(assemblyName, ref parts))
197197
{
198198
result = new(parts);
199199
return true;

src/libraries/System.Reflection.Metadata/tests/Metadata/AssemblyNameInfoTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ public void RetargetableIsPropagated()
9898
public void EscapedSquareBracketIsNotAllowedInTheName()
9999
=> Assert.False(AssemblyNameInfo.TryParse("Esc\\[aped".AsSpan(), out _));
100100

101+
[Fact]
102+
public void EmptyInputIsInvalid()
103+
{
104+
Assert.False(AssemblyNameInfo.TryParse("".AsSpan(), out _));
105+
Assert.Throws<ArgumentException>(() => AssemblyNameInfo.Parse("".AsSpan()));
106+
}
107+
101108
static void Roundtrip(AssemblyName source)
102109
{
103110
AssemblyNameInfo parsed = AssemblyNameInfo.Parse(source.FullName.AsSpan());

0 commit comments

Comments
 (0)