diff --git a/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml b/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml index 0cde3c038ed65..8d0590c992196 100644 --- a/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml +++ b/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml @@ -129,6 +129,14 @@ extends: SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Send TextEncodingFuzzer to OneFuzz + - task: onefuzz-task@0 + inputs: + onefuzzOSes: 'Windows' + env: + onefuzzDropDirectory: $(fuzzerProject)/deployment/TypeNameFuzzer + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + displayName: Send TypeNameFuzzer to OneFuzz + - task: onefuzz-task@0 inputs: onefuzzOSes: 'Windows' diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/typename.dict b/src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/typename.dict new file mode 100644 index 0000000000000..1380ac70af0ec --- /dev/null +++ b/src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/typename.dict @@ -0,0 +1,26 @@ +" " +"0" +"," +"[*[" +" System.Int32 " +"System.Int32&&" +"System.Int32[,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]" +"System.Int32[*******************************]" +"System.Int32[][][][][][][][][][][][][][][][][][][][]" +".NoNamespace" +"Namespace.Ko\xC5\x9B\xC4\x87" +"Declaring+Nested" +"Declaring+Deep+Deeper+Nested" +"Declaring+Deep+Deeper+Nested, Lib" +"MyNamespace.MyType+NestedType" +"Pointer*******" +"Generic`1[[A]]" +"Generic`2[[A],[B]]" +"Generic`3[[A],[B],[C]]" +"Generic`3[A,B,C]" +"Generic`2[[System.Int32, mscorlib, Version=4.0.0, Culture=neutral, PublicKeyToken=b77a5c561934],[System.Boolean, mscorlib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" +"Generic`3[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089], System.Boolean, [System.Byte, other, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" +"TypeNameWith\\TheEscapingCharacter" +"TypeNameWith[[]]NestedSquareBrackets" +"TypeNameWith\\[]+*&,AllSpecialCharacters" +"System.Reflection.Metadata.Tests.TypeNameTests+NestedGeneric_0`1+NestedGeneric_1`2+NestedGeneric_2`3+NestedNonGeneric_3[[System.Int32, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int16, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Byte, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.SByte, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Reflection.Metadata.Tests, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" \ No newline at end of file diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs new file mode 100644 index 0000000000000..f8b3e96083707 --- /dev/null +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Reflection.Metadata; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; + +namespace DotnetFuzzing.Fuzzers +{ + internal sealed class TypeNameFuzzer : IFuzzer + { + public string[] TargetAssemblies { get; } = ["System.Reflection.Metadata"]; + public string[] TargetCoreLibPrefixes => []; + public string Dictionary => "typename.dict"; + + public void FuzzTarget(ReadOnlySpan bytes) + { + Span charSpan = new char[Encoding.UTF8.GetCharCount(bytes)]; + Encoding.UTF8.GetChars(bytes, charSpan); + using var poisonAfter = PooledBoundedMemory.Rent(charSpan, PoisonPagePlacement.After); + using var poisonBefore = PooledBoundedMemory.Rent(charSpan, PoisonPagePlacement.Before); + + Test(poisonAfter.Span); + Test(poisonBefore.Span); + } + + private static void Test(Span testSpan) + { + if (TypeName.TryParse(testSpan, out TypeName? result1)) + { + TypeName result2 = TypeName.Parse(testSpan); + Assert.Equal(result1.Name, result2.Name); + Assert.Equal(result1.FullName, result2.FullName); + Assert.Equal(result1.AssemblyQualifiedName, result2.AssemblyQualifiedName); + Assert.Equal(result1.IsSimple, result2.IsSimple); + Assert.Equal(result1.IsNested, result2.IsNested); + Assert.Equal(result1.IsArray, result2.IsArray); + Assert.Equal(result1.GetNodeCount(), result2.GetNodeCount()); + if (result1.AssemblyName != null) + { + Assert.Equal(result1.AssemblyName.Name, result2.AssemblyName!.Name); + Assert.Equal(result1.AssemblyName.FullName, result2.AssemblyName.FullName); + Assert.Equal(result1.AssemblyName.CultureName, result2.AssemblyName.CultureName); + Assert.Equal(result1.AssemblyName.Version?.ToString(), result2.AssemblyName.Version?.ToString()); + } + else + { + Assert.Equal(result1.AssemblyName, result2.AssemblyName); + } + } + else + { + try + { + TypeName.Parse(testSpan); + Assert.Equal(true, false); // should never succeed + } + catch (ArgumentException) { } + catch (InvalidOperationException) { } + } + } + } +}