From b89443d925b9981f42338bafa4144c175544824b Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 3 Sep 2024 15:31:44 -0700 Subject: [PATCH 1/2] Add NrbfDecoderFuzzer --- .../libraries/fuzzing/deploy-to-onefuzz.yml | 8 ++ src/libraries/Fuzzing/DotnetFuzzing/Assert.cs | 11 ++ .../Dictionaries/nrbfdecoder.dict | 16 +++ .../DotnetFuzzing/DotnetFuzzing.csproj | 1 + .../Fuzzers/AssemblyNameInfoFuzzer.cs | 12 +- .../Fuzzers/NrbfDecoderFuzzer.cs | 107 ++++++++++++++++++ .../DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs | 4 +- 7 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/nrbfdecoder.dict create mode 100644 src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs diff --git a/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml b/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml index 8d0590c992196..49e7e517d9c33 100644 --- a/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml +++ b/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml @@ -105,6 +105,14 @@ extends: SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Send JsonDocumentFuzzer to OneFuzz + - task: onefuzz-task@0 + inputs: + onefuzzOSes: 'Windows' + env: + onefuzzDropDirectory: $(fuzzerProject)/deployment/NrbfDecoderFuzzer + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + displayName: Send NrbfDecoderFuzzer to OneFuzz + - task: onefuzz-task@0 inputs: onefuzzOSes: 'Windows' diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Assert.cs b/src/libraries/Fuzzing/DotnetFuzzing/Assert.cs index 810174ccbc131..a5f2a9dd1d195 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Assert.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Assert.cs @@ -18,6 +18,17 @@ static void Throw(T expected, T actual) => throw new Exception($"Expected={expected} Actual={actual}"); } + public static void NotNull(T value) + { + if (value == null) + { + ThrowNull(); + } + + static void ThrowNull() => + throw new Exception("Value is null"); + } + public static void SequenceEqual(ReadOnlySpan expected, ReadOnlySpan actual) { if (!expected.SequenceEqual(actual)) diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/nrbfdecoder.dict b/src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/nrbfdecoder.dict new file mode 100644 index 0000000000000..1b8f14f961cc1 --- /dev/null +++ b/src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/nrbfdecoder.dict @@ -0,0 +1,16 @@ +# "Hello World!" +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x06\x01\x00\x00\x00\x0C\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64\x21\x0B" +# new DateTime(2024, 2, 29) +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x00\x0F\x53\x79\x73\x74\x65\x6D\x2E\x44\x61\x74\x65\x54\x69\x6D\x65\x02\x00\x00\x00\x05\x74\x69\x63\x6B\x73\x08\x64\x61\x74\x65\x44\x61\x74\x61\x00\x00\x09\x10\x00\x00\x60\x5F\xB9\x38\xDC\x08\x00\x00\x60\x5F\xB9\x38\xDC\x08\x0B" +# new int[] { 1, 2, 3 } +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x0F\x01\x00\x00\x00\x03\x00\x00\x00\x08\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0B" +# new object[] { int.MaxValue, "string", null } +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x10\x01\x00\x00\x00\x03\x00\x00\x00\x08\x08\xFF\xFF\xFF\x7F\x06\x02\x00\x00\x00\x06\x73\x74\x72\x69\x6E\x67\x0A\x0B" +# new int?[Array.MaxLength] (plenty of nulls) +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x07\x01\x00\x00\x00\x00\x01\x00\x00\x00\xC7\xFF\xFF\x7F\x03\x6E\x53\x79\x73\x74\x65\x6D\x2E\x4E\x75\x6C\x6C\x61\x62\x6C\x65\x60\x31\x5B\x5B\x53\x79\x73\x74\x65\x6D\x2E\x49\x6E\x74\x33\x32\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x5D\x0E\xC7\xFF\xFF\x7F\x0B" +# [["jagged", "array"], ["of", "strings"]] +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x07\x01\x00\x00\x00\x01\x01\x00\x00\x00\x02\x00\x00\x00\x06\x09\x02\x00\x00\x00\x09\x03\x00\x00\x00\x11\x02\x00\x00\x00\x02\x00\x00\x00\x06\x04\x00\x00\x00\x06\x6A\x61\x67\x67\x65\x64\x06\x05\x00\x00\x00\x05\x61\x72\x72\x61\x79\x11\x03\x00\x00\x00\x02\x00\x00\x00\x06\x06\x00\x00\x00\x02\x6F\x66\x06\x07\x00\x00\x00\x07\x73\x74\x72\x69\x6E\x67\x73\x0B" +# new Dictionary { { "1", true }, { "2", false } } +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x00\xE3\x01\x53\x79\x73\x74\x65\x6D\x2E\x43\x6F\x6C\x6C\x65\x63\x74\x69\x6F\x6E\x73\x2E\x47\x65\x6E\x65\x72\x69\x63\x2E\x44\x69\x63\x74\x69\x6F\x6E\x61\x72\x79\x60\x32\x5B\x5B\x53\x79\x73\x74\x65\x6D\x2E\x53\x74\x72\x69\x6E\x67\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x2C\x5B\x53\x79\x73\x74\x65\x6D\x2E\x42\x6F\x6F\x6C\x65\x61\x6E\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x5D\x04\x00\x00\x00\x07\x56\x65\x72\x73\x69\x6F\x6E\x08\x43\x6F\x6D\x70\x61\x72\x65\x72\x08\x48\x61\x73\x68\x53\x69\x7A\x65\x0D\x4B\x65\x79\x56\x61\x6C\x75\x65\x50\x61\x69\x72\x73\x00\x03\x00\x03\x08\x92\x01\x53\x79\x73\x74\x65\x6D\x2E\x43\x6F\x6C\x6C\x65\x63\x74\x69\x6F\x6E\x73\x2E\x47\x65\x6E\x65\x72\x69\x63\x2E\x47\x65\x6E\x65\x72\x69\x63\x45\x71\x75\x61\x6C\x69\x74\x79\x43\x6F\x6D\x70\x61\x72\x65\x72\x60\x31\x5B\x5B\x53\x79\x73\x74\x65\x6D\x2E\x53\x74\x72\x69\x6E\x67\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x5D\x08\xE7\x01\x53\x79\x73\x74\x65\x6D\x2E\x43\x6F\x6C\x6C\x65\x63\x74\x69\x6F\x6E\x73\x2E\x47\x65\x6E\x65\x72\x69\x63\x2E\x4B\x65\x79\x56\x61\x6C\x75\x65\x50\x61\x69\x72\x60\x32\x5B\x5B\x53\x79\x73\x74\x65\x6D\x2E\x53\x74\x72\x69\x6E\x67\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x2C\x5B\x53\x79\x73\x74\x65\x6D\x2E\x42\x6F\x6F\x6C\x65\x61\x6E\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x5D\x5B\x5D\x02\x00\x00\x00\x09\x02\x00\x00\x00\x03\x00\x00\x00\x09\x03\x00\x00\x00\x04\x02\x00\x00\x00\x92\x01\x53\x79\x73\x74\x65\x6D\x2E\x43\x6F\x6C\x6C\x65\x63\x74\x69\x6F\x6E\x73\x2E\x47\x65\x6E\x65\x72\x69\x63\x2E\x47\x65\x6E\x65\x72\x69\x63\x45\x71\x75\x61\x6C\x69\x74\x79\x43\x6F\x6D\x70\x61\x72\x65\x72\x60\x31\x5B\x5B\x53\x79\x73\x74\x65\x6D\x2E\x53\x74\x72\x69\x6E\x67\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x5D\x00\x00\x00\x00\x07\x03\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xE5\x01\x53\x79\x73\x74\x65\x6D\x2E\x43\x6F\x6C\x6C\x65\x63\x74\x69\x6F\x6E\x73\x2E\x47\x65\x6E\x65\x72\x69\x63\x2E\x4B\x65\x79\x56\x61\x6C\x75\x65\x50\x61\x69\x72\x60\x32\x5B\x5B\x53\x79\x73\x74\x65\x6D\x2E\x53\x74\x72\x69\x6E\x67\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x2C\x5B\x53\x79\x73\x74\x65\x6D\x2E\x42\x6F\x6F\x6C\x65\x61\x6E\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x5D\x04\xFC\xFF\xFF\xFF\xE5\x01\x53\x79\x73\x74\x65\x6D\x2E\x43\x6F\x6C\x6C\x65\x63\x74\x69\x6F\x6E\x73\x2E\x47\x65\x6E\x65\x72\x69\x63\x2E\x4B\x65\x79\x56\x61\x6C\x75\x65\x50\x61\x69\x72\x60\x32\x5B\x5B\x53\x79\x73\x74\x65\x6D\x2E\x53\x74\x72\x69\x6E\x67\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x2C\x5B\x53\x79\x73\x74\x65\x6D\x2E\x42\x6F\x6F\x6C\x65\x61\x6E\x2C\x20\x6D\x73\x63\x6F\x72\x6C\x69\x62\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x34\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5D\x5D\x02\x00\x00\x00\x03\x6B\x65\x79\x05\x76\x61\x6C\x75\x65\x01\x00\x01\x06\x05\x00\x00\x00\x01\x31\x01\x01\xFA\xFF\xFF\xFF\xFC\xFF\xFF\xFF\x06\x07\x00\x00\x00\x01\x32\x00\x0B" +# new ComplexType2D { I = 1, J = 2 } (non-system class) +"\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x0C\x02\x00\x00\x00\x3D\x42\x66\x44\x65\x6D\x6F\x2C\x20\x56\x65\x72\x73\x69\x6F\x6E\x3D\x31\x2E\x30\x2E\x30\x2E\x30\x2C\x20\x43\x75\x6C\x74\x75\x72\x65\x3D\x6E\x65\x75\x74\x72\x61\x6C\x2C\x20\x50\x75\x62\x6C\x69\x63\x4B\x65\x79\x54\x6F\x6B\x65\x6E\x3D\x6E\x75\x6C\x6C\x05\x01\x00\x00\x00\x14\x42\x66\x44\x65\x6D\x6F\x2E\x43\x6F\x6D\x70\x6C\x65\x78\x54\x79\x70\x65\x32\x44\x02\x00\x00\x00\x01\x49\x01\x4A\x00\x00\x08\x08\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x0B" \ No newline at end of file diff --git a/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj b/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj index 293af0122bfb4..86c7c058a8fff 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj +++ b/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj @@ -13,6 +13,7 @@ + diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/AssemblyNameInfoFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/AssemblyNameInfoFuzzer.cs index 9ce1bd255c7b6..d166726665afe 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/AssemblyNameInfoFuzzer.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/AssemblyNameInfoFuzzer.cs @@ -24,15 +24,15 @@ public void FuzzTarget(ReadOnlySpan bytes) using PooledBoundedMemory inputPoisonedBefore = PooledBoundedMemory.Rent(chars, PoisonPagePlacement.Before); using PooledBoundedMemory inputPoisonedAfter = PooledBoundedMemory.Rent(chars, PoisonPagePlacement.After); - Test(inputPoisonedBefore); - Test(inputPoisonedAfter); + Test(inputPoisonedBefore.Span); + Test(inputPoisonedAfter.Span); } - private static void Test(PooledBoundedMemory inputPoisoned) + private static void Test(Span span) { - if (AssemblyNameInfo.TryParse(inputPoisoned.Span, out AssemblyNameInfo? fromTryParse)) + if (AssemblyNameInfo.TryParse(span, out AssemblyNameInfo? fromTryParse)) { - AssemblyNameInfo fromParse = AssemblyNameInfo.Parse(inputPoisoned.Span); + AssemblyNameInfo fromParse = AssemblyNameInfo.Parse(span); Assert.Equal(fromTryParse.Name, fromParse.Name); Assert.Equal(fromTryParse.FullName, fromParse.FullName); @@ -66,7 +66,7 @@ private static void Test(PooledBoundedMemory inputPoisoned) { try { - _ = AssemblyNameInfo.Parse(inputPoisoned.Span); + _ = AssemblyNameInfo.Parse(span); } catch (ArgumentException) { diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs new file mode 100644 index 0000000000000..15d7d147bc239 --- /dev/null +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs @@ -0,0 +1,107 @@ +// 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.Formats.Nrbf; +using System.Runtime.Serialization; +using System.Text; + +namespace DotnetFuzzing.Fuzzers +{ + internal sealed class NrbfDecoderFuzzer : IFuzzer + { + public string[] TargetAssemblies { get; } = ["System.Formats.Nrbf"]; + + public string[] TargetCoreLibPrefixes => []; + + public string Dictionary => "nrbfdecoder.dict"; + + public void FuzzTarget(ReadOnlySpan bytes) + { + using PooledBoundedMemory inputPoisoned = PooledBoundedMemory.Rent(bytes, PoisonPagePlacement.After); + using MemoryStream stream = new MemoryStream(inputPoisoned.Memory.ToArray()); + + if (NrbfDecoder.StartsWithPayloadHeader(inputPoisoned.Span)) + { + try + { + SerializationRecord record = NrbfDecoder.Decode(stream, out IReadOnlyDictionary recordMap); + switch (record.RecordType) + { + case SerializationRecordType.ArraySingleObject: + SZArrayRecord arrayObj = (SZArrayRecord)record; + object?[] objArray = arrayObj.GetArray(); + Assert.Equal(arrayObj.Length, objArray.Length); + Assert.Equal(1, arrayObj.Rank); + break; + case SerializationRecordType.ArraySingleString: + SZArrayRecord arrayString = (SZArrayRecord)record; + string?[] array = arrayString.GetArray(); + Assert.Equal(arrayString.Length, array.Length); + Assert.Equal(1, arrayString.Rank); + Assert.Equal(true, arrayString.TypeNameMatches(typeof(string[]))); + break; + case SerializationRecordType.ArraySinglePrimitive: + case SerializationRecordType.BinaryArray: + ArrayRecord arrayBinary = (ArrayRecord)record; + Assert.NotNull(arrayBinary.TypeName); + break; + case SerializationRecordType.BinaryObjectString: + _ = ((PrimitiveTypeRecord)record).Value; + break; + case SerializationRecordType.ClassWithId: + case SerializationRecordType.ClassWithMembersAndTypes: + case SerializationRecordType.SystemClassWithMembersAndTypes: + { + ClassRecord classRecord = (ClassRecord)record; + Assert.NotNull(classRecord.TypeName); + + foreach (string name in classRecord.MemberNames) + { + Assert.Equal(true, classRecord.HasMember(name)); + } + } break; + case SerializationRecordType.MemberPrimitiveTyped: + PrimitiveTypeRecord primitiveType = (PrimitiveTypeRecord)record; + Assert.NotNull(primitiveType.Value); + break; + case SerializationRecordType.MemberReference: + Assert.NotNull(record.TypeName); + break; + case SerializationRecordType.BinaryLibrary: + case SerializationRecordType.ObjectNull: + case SerializationRecordType.ObjectNullMultiple: + case SerializationRecordType.ObjectNullMultiple256: + Assert.Equal(default, record.Id); + break; + case SerializationRecordType.MessageEnd: + case SerializationRecordType.SerializedStreamHeader: + // case SerializationRecordType.ClassWithMembers: will cause NotSupportedException + // case SerializationRecordType.SystemClassWithMembers: will cause NotSupportedException + default: + throw new Exception("Unexpected RecordType"); + } + } + catch (SerializationException) { /* Reading from the stream encountered invalid NRBF data.*/ } + catch (NotSupportedException) { /* Reading from the stream encountered unsupported records */ } + catch (DecoderFallbackException) { /* Reading from the stream encountered an invalid UTF8 sequence. */ } + catch (EndOfStreamException) { /* The end of the stream was reached before reading SerializationRecordType.MessageEnd record. */ } + } + else + { + try + { + NrbfDecoder.Decode(stream); + throw new Exception("Decoding supposed to fail!"); + } + catch (SerializationException) { /* Everything has to start with a header */ } + catch (NotSupportedException) { /* Reading from the stream encountered unsupported records */ } + catch (EndOfStreamException) { /* The end of the stream was reached before reading SerializationRecordType.MessageEnd record. */ } + catch (DecoderFallbackException) { /* Reading from the stream encountered an invalid UTF8 sequence. */ } + // below exceptions are not expected + catch (FormatException) { /* Temporarily catch this until its fixed */ } + catch (IOException) { /* Temporarily catch this until its fixed */ } + } + } + } +} diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs index f8b3e96083707..0a189da4f18af 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TypeNameFuzzer.cs @@ -3,8 +3,6 @@ using System.Buffers; using System.Reflection.Metadata; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; using System.Text; namespace DotnetFuzzing.Fuzzers @@ -55,7 +53,7 @@ private static void Test(Span testSpan) try { TypeName.Parse(testSpan); - Assert.Equal(true, false); // should never succeed + throw new Exception("Parsing was supposed to fail!"); } catch (ArgumentException) { } catch (InvalidOperationException) { } From 45fb54e8ac6a90f4eb1f2ed4d645ac093a012a84 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 5 Sep 2024 14:51:41 -0700 Subject: [PATCH 2/2] Reference System.Formats.Nrbf project directly, remove catch blocks that not needed anymore with the fix --- .../DotnetFuzzing/DotnetFuzzing.csproj | 5 +++- .../Fuzzers/NrbfDecoderFuzzer.cs | 26 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj b/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj index 86c7c058a8fff..82f54f412ace6 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj +++ b/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj @@ -13,7 +13,6 @@ - @@ -31,4 +30,8 @@ + + + + diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs index 15d7d147bc239..46da18e4b0fc8 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs @@ -18,10 +18,18 @@ internal sealed class NrbfDecoderFuzzer : IFuzzer public void FuzzTarget(ReadOnlySpan bytes) { - using PooledBoundedMemory inputPoisoned = PooledBoundedMemory.Rent(bytes, PoisonPagePlacement.After); - using MemoryStream stream = new MemoryStream(inputPoisoned.Memory.ToArray()); + using PooledBoundedMemory inputPoisonedAfter = PooledBoundedMemory.Rent(bytes, PoisonPagePlacement.After); + using PooledBoundedMemory inputPoisonedBefore = PooledBoundedMemory.Rent(bytes, PoisonPagePlacement.Before); + using MemoryStream streamAfter = new MemoryStream(inputPoisonedAfter.Memory.ToArray()); + using MemoryStream streamBefore = new MemoryStream(inputPoisonedBefore.Memory.ToArray()); - if (NrbfDecoder.StartsWithPayloadHeader(inputPoisoned.Span)) + Test(inputPoisonedAfter.Span, streamAfter); + Test(inputPoisonedBefore.Span, streamBefore); + } + + private static void Test(Span testSpan, MemoryStream stream) + { + if (NrbfDecoder.StartsWithPayloadHeader(testSpan)) { try { @@ -52,7 +60,6 @@ public void FuzzTarget(ReadOnlySpan bytes) case SerializationRecordType.ClassWithId: case SerializationRecordType.ClassWithMembersAndTypes: case SerializationRecordType.SystemClassWithMembersAndTypes: - { ClassRecord classRecord = (ClassRecord)record; Assert.NotNull(classRecord.TypeName); @@ -60,7 +67,7 @@ public void FuzzTarget(ReadOnlySpan bytes) { Assert.Equal(true, classRecord.HasMember(name)); } - } break; + break; case SerializationRecordType.MemberPrimitiveTyped: PrimitiveTypeRecord primitiveType = (PrimitiveTypeRecord)record; Assert.NotNull(primitiveType.Value); @@ -69,6 +76,8 @@ public void FuzzTarget(ReadOnlySpan bytes) Assert.NotNull(record.TypeName); break; case SerializationRecordType.BinaryLibrary: + Assert.Equal(false, record.Id.Equals(default)); + break; case SerializationRecordType.ObjectNull: case SerializationRecordType.ObjectNullMultiple: case SerializationRecordType.ObjectNullMultiple256: @@ -86,8 +95,9 @@ public void FuzzTarget(ReadOnlySpan bytes) catch (NotSupportedException) { /* Reading from the stream encountered unsupported records */ } catch (DecoderFallbackException) { /* Reading from the stream encountered an invalid UTF8 sequence. */ } catch (EndOfStreamException) { /* The end of the stream was reached before reading SerializationRecordType.MessageEnd record. */ } + catch (IOException) { /* An I/O error occurred. */ } } - else + else { try { @@ -97,10 +107,6 @@ public void FuzzTarget(ReadOnlySpan bytes) catch (SerializationException) { /* Everything has to start with a header */ } catch (NotSupportedException) { /* Reading from the stream encountered unsupported records */ } catch (EndOfStreamException) { /* The end of the stream was reached before reading SerializationRecordType.MessageEnd record. */ } - catch (DecoderFallbackException) { /* Reading from the stream encountered an invalid UTF8 sequence. */ } - // below exceptions are not expected - catch (FormatException) { /* Temporarily catch this until its fixed */ } - catch (IOException) { /* Temporarily catch this until its fixed */ } } } }