From 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 4 Oct 2024 19:49:00 -0700 Subject: [PATCH] Update fuzzing infra to build and use the shared framework (#108555) * Update fuzzing infra to build and use the shared framework * Update deploy-to-onefuzz pipeline * Apply suggestions from code review Co-authored-by: Miha Zupan --------- Co-authored-by: Miha Zupan --- .../libraries/fuzzing/deploy-to-onefuzz.yml | 7 ++-- .../System/Buffers/BoundedMemory.Default.cs | 4 +-- .../System/Buffers/BoundedMemory.Windows.cs | 15 +++++---- src/libraries/Fuzzing/Directory.Build.props | 9 +++--- src/libraries/Fuzzing/Directory.Build.targets | 2 -- src/libraries/Fuzzing/DotnetFuzzing.sln | 12 ++++--- .../DotnetFuzzing/DotnetFuzzing.csproj | 32 +++++++++++++------ .../DotnetFuzzing/Fuzzers/Base64Fuzzer.cs | 6 ++-- .../DotnetFuzzing/Fuzzers/Base64UrlFuzzer.cs | 2 +- .../Fuzzers/NrbfDecoderFuzzer.cs | 2 +- .../Fuzzers/TextEncodingFuzzer.cs | 5 ++- .../Fuzzing/DotnetFuzzing/Program.cs | 6 ++-- src/libraries/Fuzzing/DotnetFuzzing/run.bat | 1 + src/libraries/Fuzzing/README.md | 22 +++++++++---- src/libraries/Fuzzing/nuget.config | 19 ----------- 15 files changed, 76 insertions(+), 68 deletions(-) delete mode 100644 src/libraries/Fuzzing/Directory.Build.targets create mode 100644 src/libraries/Fuzzing/DotnetFuzzing/run.bat delete mode 100644 src/libraries/Fuzzing/nuget.config diff --git a/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml b/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml index 49e7e517d9c336..9f2b06ec638bfd 100644 --- a/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml +++ b/eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml @@ -35,12 +35,13 @@ extends: - powershell: | cd $(Build.SourcesDirectory) - ./build.cmd clr+libs+packs+host -rc Checked -c Debug + ./build.cmd clr+libs -rc Checked -c Debug displayName: Build runtime (checked + debug) - powershell: | cd $(fuzzerProject) - $(dotnetPath) publish -o publish + $(dotnetPath) build-server shutdown + $(dotnetPath) build displayName: Build Fuzzing targets - powershell: | @@ -50,7 +51,7 @@ extends: - powershell: | cd $(fuzzerProject) - publish/DotnetFuzzing.exe prepare-onefuzz deployment + ./run.bat displayName: Prepare OneFuzz deployment # OneFuzz can't currently handle a single deployment where multiple jobs share similar assemblies/pdbs. diff --git a/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Default.cs b/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Default.cs index fc8134859f92fb..a8169aece5904a 100644 --- a/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Default.cs +++ b/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Default.cs @@ -7,7 +7,7 @@ namespace System.Buffers { public static unsafe partial class BoundedMemory { - private static DefaultImplementation AllocateWithoutDataPopulationDefault(int elementCount, PoisonPagePlacement placement) where T : unmanaged + private static DefaultImplementation AllocateWithoutDataPopulationDefault(int elementCount, PoisonPagePlacement _) where T : unmanaged { // On non-Windows platforms, we don't yet have support for changing the permissions of individual pages. // We'll instead use AllocHGlobal / FreeHGlobal to carve out a r+w section of unmanaged memory. @@ -143,4 +143,4 @@ protected override bool ReleaseHandle() } } } -} \ No newline at end of file +} diff --git a/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Windows.cs b/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Windows.cs index bb8843c757abdc..cdfb30f6fd9dba 100644 --- a/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Windows.cs +++ b/src/libraries/Common/tests/TestUtilities/System/Buffers/BoundedMemory.Windows.cs @@ -30,12 +30,15 @@ private static WindowsImplementation AllocateWithoutDataPopulationWindows( } // Reserve and commit the entire range as NOACCESS. - - VirtualAllocHandle handle = VirtualAllocHandle.Allocate( - lpAddress: IntPtr.Zero, - dwSize: (IntPtr)totalBytesToAllocate /* cast throws OverflowException if out of range */, - flAllocationType: VirtualAllocAllocationType.MEM_RESERVE | VirtualAllocAllocationType.MEM_COMMIT, - flProtect: VirtualAllocProtection.PAGE_NOACCESS); + VirtualAllocHandle handle; + checked + { + handle = VirtualAllocHandle.Allocate( + lpAddress: IntPtr.Zero, + dwSize: (IntPtr)totalBytesToAllocate /* cast throws OverflowException if out of range */, + flAllocationType: VirtualAllocAllocationType.MEM_RESERVE | VirtualAllocAllocationType.MEM_COMMIT, + flProtect: VirtualAllocProtection.PAGE_NOACCESS); + } if (handle == null || handle.IsInvalid) { diff --git a/src/libraries/Fuzzing/Directory.Build.props b/src/libraries/Fuzzing/Directory.Build.props index bc4d985cad2531..8843a28c9f8cfd 100644 --- a/src/libraries/Fuzzing/Directory.Build.props +++ b/src/libraries/Fuzzing/Directory.Build.props @@ -1,8 +1,7 @@  - 10.0 - net$(NetCoreAppCurrentVersion) - $(NetCoreAppCurrentVersion).0 - ..\..\Common\tests\TestUtilities + true + ..\..\Common\tests\TestUtilities - + + \ No newline at end of file diff --git a/src/libraries/Fuzzing/Directory.Build.targets b/src/libraries/Fuzzing/Directory.Build.targets deleted file mode 100644 index c1df2220ddc6e3..00000000000000 --- a/src/libraries/Fuzzing/Directory.Build.targets +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/libraries/Fuzzing/DotnetFuzzing.sln b/src/libraries/Fuzzing/DotnetFuzzing.sln index bfd3d21a7148b1..204fe7b3f812fd 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing.sln +++ b/src/libraries/Fuzzing/DotnetFuzzing.sln @@ -9,12 +9,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore ..\..\..\eng\pipelines\libraries\fuzzing\deploy-to-onefuzz.yml = ..\..\..\eng\pipelines\libraries\fuzzing\deploy-to-onefuzz.yml Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - nuget.config = nuget.config - README.md = README.md OneFuzz.md = OneFuzz.md + README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Formats.Nrbf", "..\System.Formats.Nrbf\src\System.Formats.Nrbf.csproj", "{034899ED-D501-46A3-A8AB-710C28C5CAB9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -25,6 +25,10 @@ Global {002673BF-11AE-4072-9CBE-FC312BF68613}.Debug|Any CPU.Build.0 = Debug|Any CPU {002673BF-11AE-4072-9CBE-FC312BF68613}.Release|Any CPU.ActiveCfg = Release|Any CPU {002673BF-11AE-4072-9CBE-FC312BF68613}.Release|Any CPU.Build.0 = Release|Any CPU + {034899ED-D501-46A3-A8AB-710C28C5CAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {034899ED-D501-46A3-A8AB-710C28C5CAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {034899ED-D501-46A3-A8AB-710C28C5CAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {034899ED-D501-46A3-A8AB-710C28C5CAB9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -32,4 +36,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0BAD3B6E-67D4-418D-A45F-49B3F79168DF} EndGlobalSection -EndGlobal \ No newline at end of file +EndGlobal diff --git a/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj b/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj index 82f54f412ace6d..a0aca709b3e679 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj +++ b/src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj @@ -2,13 +2,16 @@ Exe - net9.0-windows - true + $(NetCoreAppCurrent) + true + win-x64 + true + $(ArtifactsDir)\bin\win-x64.Debug\corehost\apphost.exe enable enable - True - $(NoWarn);CA2252 + $(NoWarn);CS1591;IL3000;SYSLIB1054;CA1512;SYSLIB5005; true + false @@ -16,16 +19,27 @@ - - - - + + + + + + + + + + + + + + + - + PreserveNewest diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64Fuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64Fuzzer.cs index 11b4c1347e9609..000718de72537e 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64Fuzzer.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64Fuzzer.cs @@ -7,7 +7,7 @@ namespace DotnetFuzzing.Fuzzers { - internal class Base64Fuzzer : IFuzzer + internal sealed class Base64Fuzzer : IFuzzer { public string[] TargetAssemblies => []; @@ -27,8 +27,8 @@ public void FuzzTarget(ReadOnlySpan bytes) Assert.Equal(OperationStatus.Done, status); Assert.Equal(bytes.Length, bytesConsumed); - Assert.Equal(true, maxEncodedLength >= bytesEncoded && maxEncodedLength - 2 <= bytesEncoded); - + Assert.Equal(true, maxEncodedLength >= bytesEncoded && maxEncodedLength - 2 <= bytesEncoded); + status = Base64.DecodeFromUtf8(encoderDest.Slice(0, bytesEncoded), decoderDest, out int bytesRead, out int bytesDecoded); Assert.Equal(OperationStatus.Done, status); diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64UrlFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64UrlFuzzer.cs index e65b2df740dba9..7aef90ce85e2ff 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64UrlFuzzer.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64UrlFuzzer.cs @@ -28,7 +28,7 @@ public void FuzzTarget(ReadOnlySpan bytes) Span decoderDest = decoderDestPoisoned.Span; { // IsFinalBlock = true - OperationStatus status = Base64Url.EncodeToChars(input, encoderDest, out int bytesConsumed, out int bytesEncoded); + OperationStatus status = Base64Url.EncodeToChars(input, encoderDest, out int bytesConsumed, out int bytesEncoded); Assert.Equal(OperationStatus.Done, status); Assert.Equal(bytes.Length, bytesConsumed); diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs index 5c6397187ff643..f217649927b159 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/NrbfDecoderFuzzer.cs @@ -117,7 +117,7 @@ private static void Test(Span testSpan, Stream stream) } } - private class NonSeekableStream : MemoryStream + private sealed class NonSeekableStream : MemoryStream { public NonSeekableStream(byte[] buffer) : base(buffer) { } public override bool CanSeek => false; diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TextEncodingFuzzer.cs b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TextEncodingFuzzer.cs index 21ac6ff72a7f32..593a80b327865a 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TextEncodingFuzzer.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/TextEncodingFuzzer.cs @@ -84,7 +84,7 @@ private static void TestWithSubstitution(ReadOnlySpan input, Encoding enco using PooledBoundedMemory chars = PooledBoundedMemory.Rent(charCount, PoisonPagePlacement.After); using PooledBoundedMemory chars2 = PooledBoundedMemory.Rent(charCount, PoisonPagePlacement.After); - + // *4 for worst case scenario (*2 for char->byte + *2 for encoding) // +2 is for possible Base64 padding with UTF7Encoding. using PooledBoundedMemory bytes = PooledBoundedMemory.Rent(charCount * 4 + 2, PoisonPagePlacement.After); @@ -153,14 +153,13 @@ private static void TestWithConvert(ReadOnlySpan input, Encoding encoding) private static void TestWithConvert(ReadOnlySpan input, Encoding encoding, int blockSize) { Decoder decoder = encoding.GetDecoder(); - Encoder encoder = encoding.GetEncoder(); int charCount = decoder.GetCharCount(input, flush: true); using PooledBoundedMemory chars = PooledBoundedMemory.Rent(charCount, PoisonPagePlacement.After); using PooledBoundedMemory chars2 = PooledBoundedMemory.Rent(charCount, PoisonPagePlacement.After); - decoder.Reset(); + decoder.Reset(); int charsUsedTotal = 0; int i = 0; diff --git a/src/libraries/Fuzzing/DotnetFuzzing/Program.cs b/src/libraries/Fuzzing/DotnetFuzzing/Program.cs index 020526ae59a435..a2e0f4d6e60a4f 100644 --- a/src/libraries/Fuzzing/DotnetFuzzing/Program.cs +++ b/src/libraries/Fuzzing/DotnetFuzzing/Program.cs @@ -50,7 +50,7 @@ DotnetFuzzing prepare-onefuzz string publishDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? Environment.CurrentDirectory; - await PrepareOneFuzzDeploymentAsync(fuzzers, publishDirectory, args[1]); + await PrepareOneFuzzDeploymentAsync(fuzzers, publishDirectory, args[1]).ConfigureAwait(false); return; } @@ -108,7 +108,7 @@ private static async Task PrepareOneFuzzDeploymentAsync(IFuzzer[] fuzzers, strin await DownloadArtifactAsync( Path.Combine(publishDirectory, "libfuzzer-dotnet.exe"), "https://github.com/Metalnem/libfuzzer-dotnet/releases/download/v2023.06.26.1359/libfuzzer-dotnet-windows.exe", - "cbc1f510caaec01b17b5e89fc780f426710acee7429151634bbf4d0c57583458"); + "cbc1f510caaec01b17b5e89fc780f426710acee7429151634bbf4d0c57583458").ConfigureAwait(false); foreach (IFuzzer fuzzer in fuzzers) { @@ -263,7 +263,7 @@ private static async Task DownloadArtifactAsync(string path, string url, string Console.WriteLine($"Downloading {Path.GetFileName(path)}"); using var client = new HttpClient(); - byte[] bytes = await client.GetByteArrayAsync(url); + byte[] bytes = await client.GetByteArrayAsync(url).ConfigureAwait(false); if (!Convert.ToHexString(SHA256.HashData(bytes)).Equals(hash, StringComparison.OrdinalIgnoreCase)) { diff --git a/src/libraries/Fuzzing/DotnetFuzzing/run.bat b/src/libraries/Fuzzing/DotnetFuzzing/run.bat new file mode 100644 index 00000000000000..9d92659e7fe4c7 --- /dev/null +++ b/src/libraries/Fuzzing/DotnetFuzzing/run.bat @@ -0,0 +1 @@ +%~dp0..\..\..\..\artifacts\bin\DotnetFuzzing\Debug\net10.0\win-x64\DotnetFuzzing.exe prepare-onefuzz deployment \ No newline at end of file diff --git a/src/libraries/Fuzzing/README.md b/src/libraries/Fuzzing/README.md index 2dbeeba7a612ff..4ac8125e915837 100644 --- a/src/libraries/Fuzzing/README.md +++ b/src/libraries/Fuzzing/README.md @@ -18,28 +18,36 @@ Useful links: ### Prerequisites -Build the runtime with the following arguments: +Build the runtime if you haven't already: ```cmd -./build.cmd clr+libs+packs+host -rc Checked -c Debug +./build.cmd clr+libs -rc release ``` + and install the SharpFuzz command line tool: ```cmd dotnet tool install --global SharpFuzz.CommandLine ``` > [!TIP] -> The project uses a checked runtime + debug libraries configuration by default. -> If you want to use a different configuration, make sure to also adjust the artifact paths in `nuget.config`. +> The project uses a `Release` runtime + `Debug` libraries configuration by default. +> Automated fuzzing runs use a `Checked` runtime + `Debug` libraries configuration by default. +> You can use any configuration locally, but `Checked` is recommended when testing changes in `System.Private.CoreLib`. ### Fuzzing locally -The `prepare-onefuzz` command will create separate directories for each fuzzing target, instrument the relevant assemblies, and generate a helper script for running them locally. -Note that this command must be ran on the published artifacts (won't work with `dotnet run`). +Build the `DotnetFuzzing` fuzzing project. It is self-contained, so it will produce `DotnetFuzzing.exe` along with a copy of all required libraries. ```cmd cd src/libraries/Fuzzing/DotnetFuzzing -dotnet publish -o publish; publish/DotnetFuzzing.exe prepare-onefuzz deployment +dotnet build +``` + +Now you can run `run.bat`, which will create separate directories for each fuzzing target, instrument the relevant assemblies, and generate a helper script for running them locally. +When iterating on changes, remember to rebuild the project again: `dotnet build; .\run.bat`. + +```cmd +run.bat ``` You can now start fuzzing by running the `local-run.bat` script in the folder of the fuzzer you are interested in. diff --git a/src/libraries/Fuzzing/nuget.config b/src/libraries/Fuzzing/nuget.config deleted file mode 100644 index efa62fa824baeb..00000000000000 --- a/src/libraries/Fuzzing/nuget.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - -