src/libraries/Fuzzing/README.md
This project contains fuzzing targets for various .NET libraries, as well as supporting code for generating OneFuzz deployments from them. Targets are instrumented using SharpFuzz, and ran using libFuzzer.
The runtime and fuzzing targets are periodically rebuilt and published to OneFuzz via deploy-to-onefuzz.yml.
Useful links:
[!NOTE] The instructions assume you are running on Windows as that is what the continuous fuzzing runs currently use.
Build the runtime with the desired configuration if you haven't already:
./build.cmd clr+libs -rc release
[!TIP] The
-rc releaseconfiguration here builds runtime inReleaseand libraries inDebugmode. Automated fuzzing runs use aCheckedruntime +Debuglibraries configuration by default. You can use any configuration locally, butCheckedis recommended when testing changes inSystem.Private.CoreLib.
Install the SharpFuzz command line tool:
dotnet tool install --global SharpFuzz.CommandLine
Build the DotnetFuzzing fuzzing project. It is self-contained, so it will produce DotnetFuzzing.exe along with a copy of all required libraries.
cd src/libraries/Fuzzing/DotnetFuzzing
dotnet build
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
Start fuzzing by running the local-run.bat script in the folder of the fuzzer you are interested in.
deployment/HttpHeadersFuzzer/local-run.bat
See the libFuzzer options documentation for more information on how to customize the fuzzing process.
For example, here is how you can run the fuzzer against a header-inputs corpus directory for 10 minutes, running multiple instances in parallel:
deployment/HttpHeadersFuzzer/local-run.bat header-inputs -timeout=30 -max_total_time=600 -jobs=5
After letting the fuzzer run for a while, you can use the generated inputs to test code coverage.
mkdir header-inputs
deployment/HttpHeadersFuzzer/local-run.bat header-inputs
.\collect-coverage.ps1 HttpHeadersFuzzer header-inputs
The HTML report can be opened from
.\coverage-report\html\index.html
To create a new fuzzing target, you need to create a new class that implements the IFuzzer interface.
See existing implementations in the Fuzzers directory for reference.
As an example, let's test that IPAddress.TryParse never throws on invalid input, and doesn't access any bytes after the end of bytes:
internal sealed class IPAddressFuzzer : IFuzzer
{
public string[] TargetAssemblies => ["System.Net.Primitives"]; // Assembly where IPAddress lives
public string[] TargetCoreLibPrefixes => [];
public void FuzzTarget(ReadOnlySpan<byte> bytes)
{
// PooledBoundedMemory is a helper class that ensures reading past the end of the buffer will trigger an access violation.
using var chars = PooledBoundedMemory<char>.Rent(MemoryMarshal.Cast<byte, char>(bytes), PoisonPagePlacement.After);
_ = IPAddress.TryParse(chars.Span, out _);
}
}
TargetAssemblies is a list of assemblies where the tested code lives and that must be instrumented.TargetCoreLibPrefixes is the same, but for types/namespaces in System.Private.CoreLib.FuzzTarget is the logic that the fuzzer will run for every test input. It should exercise code from the target assemblies.Once you've created the new target, you can follow instructions above to run it locally. Targets are discovered via reflection, so they will automatically become available for local runs and continuous fuzzing in CI.
The program accepts two arguments: the name of the fuzzer and the path to a sample input file / directory.
To run the HttpHeaders target against the inputs directory, use the following command:
cd src/libraries/Fuzzing/DotnetFuzzing
dotnet run HttpHeadersFuzzer inputs
This can be useful when debugging a crash.