Skip to content

Commit

Permalink
Update fuzzing infra to build and use the shared framework (#108555)
Browse files Browse the repository at this point in the history
* 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 <mihazupan.zupan1@gmail.com>

---------

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
  • Loading branch information
buyaa-n and MihaZupan authored Oct 5, 2024
1 parent 6160518 commit 0372b50
Show file tree
Hide file tree
Showing 15 changed files with 76 additions and 68 deletions.
7 changes: 4 additions & 3 deletions eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace System.Buffers
{
public static unsafe partial class BoundedMemory
{
private static DefaultImplementation<T> AllocateWithoutDataPopulationDefault<T>(int elementCount, PoisonPagePlacement placement) where T : unmanaged
private static DefaultImplementation<T> AllocateWithoutDataPopulationDefault<T>(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.
Expand Down Expand Up @@ -143,4 +143,4 @@ protected override bool ReleaseHandle()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ private static WindowsImplementation<T> AllocateWithoutDataPopulationWindows<T>(
}

// 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)
{
Expand Down
9 changes: 4 additions & 5 deletions src/libraries/Fuzzing/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<Project>
<PropertyGroup>
<NetCoreAppCurrentVersion>10.0</NetCoreAppCurrentVersion>
<NetCoreAppCurrent>net$(NetCoreAppCurrentVersion)</NetCoreAppCurrent>
<ProductVersion>$(NetCoreAppCurrentVersion).0</ProductVersion>
<TestUtilities>..\..\Common\tests\TestUtilities</TestUtilities>
<IsTestSupportProject>true</IsTestSupportProject>
<TestUtilities>..\..\Common\tests\TestUtilities</TestUtilities>
</PropertyGroup>
</Project>
<Import Project="..\Directory.Build.props" />
</Project>
2 changes: 0 additions & 2 deletions src/libraries/Fuzzing/Directory.Build.targets

This file was deleted.

12 changes: 8 additions & 4 deletions src/libraries/Fuzzing/DotnetFuzzing.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,11 +25,15 @@ 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
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0BAD3B6E-67D4-418D-A45F-49B3F79168DF}
EndGlobalSection
EndGlobal
EndGlobal
32 changes: 23 additions & 9 deletions src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,44 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<PublishSelfContained>true</PublishSelfContained>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<UseAppHost>true</UseAppHost>
<AppHostSourcePath>$(ArtifactsDir)\bin\win-x64.Debug\corehost\apphost.exe</AppHostSourcePath>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnablePreviewFeatures>True</EnablePreviewFeatures>
<NoWarn>$(NoWarn);CA2252</NoWarn>
<NoWarn>$(NoWarn);CS1591;IL3000;SYSLIB1054;CA1512;SYSLIB5005;</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableTrimAnalyzer>false</EnableTrimAnalyzer>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="SharpFuzz" Version="2.1.1" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Update="Microsoft.NETCore.App" RuntimeFrameworkVersion="$(ProductVersion)-dev" />
</ItemGroup>

<ItemGroup>
<Compile Include="Assert.cs" />
<Compile Include="Fuzzers\AssemblyNameInfoFuzzer.cs" />
<Compile Include="Fuzzers\Base64Fuzzer.cs" />
<Compile Include="Fuzzers\Base64UrlFuzzer.cs" />
<Compile Include="Fuzzers\HttpHeadersFuzzer.cs" />
<Compile Include="Fuzzers\JsonDocumentFuzzer.cs" />
<Compile Include="Fuzzers\NrbfDecoderFuzzer.cs" />
<Compile Include="Fuzzers\SearchValuesByteCharFuzzer.cs" />
<Compile Include="Fuzzers\SearchValuesStringFuzzer.cs" />
<Compile Include="Fuzzers\TextEncodingFuzzer.cs" />
<Compile Include="Fuzzers\TypeNameFuzzer.cs" />
<Compile Include="Fuzzers\UTF8Fuzzer.cs" />
<Compile Include="IFuzzer.cs" />
<Compile Include="PooledBoundedMemory.cs" />
<Compile Include="Program.cs" />
<Compile Include="$(TestUtilities)\System\Buffers\BoundedMemory.*" Link="TestUtilities\%(Filename)%(Extension)" />
<Compile Include="$(TestUtilities)\System\Buffers\PoisonPagePlacement.cs" Link="TestUtilities\PoisonPagePlacement.cs" />
</ItemGroup>

<ItemGroup>
<None Update="Dictionaries\*">
<None Include="Dictionaries\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64Fuzzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace DotnetFuzzing.Fuzzers
{
internal class Base64Fuzzer : IFuzzer
internal sealed class Base64Fuzzer : IFuzzer
{
public string[] TargetAssemblies => [];

Expand All @@ -27,8 +27,8 @@ public void FuzzTarget(ReadOnlySpan<byte> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void FuzzTarget(ReadOnlySpan<byte> bytes)
Span<byte> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private static void Test(Span<byte> testSpan, Stream stream)
}
}

private class NonSeekableStream : MemoryStream
private sealed class NonSeekableStream : MemoryStream
{
public NonSeekableStream(byte[] buffer) : base(buffer) { }
public override bool CanSeek => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private static void TestWithSubstitution(ReadOnlySpan<byte> input, Encoding enco

using PooledBoundedMemory<char> chars = PooledBoundedMemory<char>.Rent(charCount, PoisonPagePlacement.After);
using PooledBoundedMemory<char> chars2 = PooledBoundedMemory<char>.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<byte> bytes = PooledBoundedMemory<byte>.Rent(charCount * 4 + 2, PoisonPagePlacement.After);
Expand Down Expand Up @@ -153,14 +153,13 @@ private static void TestWithConvert(ReadOnlySpan<byte> input, Encoding encoding)
private static void TestWithConvert(ReadOnlySpan<byte> input, Encoding encoding, int blockSize)
{
Decoder decoder = encoding.GetDecoder();
Encoder encoder = encoding.GetEncoder();

int charCount = decoder.GetCharCount(input, flush: true);

using PooledBoundedMemory<char> chars = PooledBoundedMemory<char>.Rent(charCount, PoisonPagePlacement.After);
using PooledBoundedMemory<char> chars2 = PooledBoundedMemory<char>.Rent(charCount, PoisonPagePlacement.After);

decoder.Reset();
decoder.Reset();
int charsUsedTotal = 0;
int i = 0;

Expand Down
6 changes: 3 additions & 3 deletions src/libraries/Fuzzing/DotnetFuzzing/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ DotnetFuzzing prepare-onefuzz <output directory>

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;
}

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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))
{
Expand Down
1 change: 1 addition & 0 deletions src/libraries/Fuzzing/DotnetFuzzing/run.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%~dp0..\..\..\..\artifacts\bin\DotnetFuzzing\Debug\net10.0\win-x64\DotnetFuzzing.exe prepare-onefuzz deployment
22 changes: 15 additions & 7 deletions src/libraries/Fuzzing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
19 changes: 0 additions & 19 deletions src/libraries/Fuzzing/nuget.config

This file was deleted.

0 comments on commit 0372b50

Please sign in to comment.