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:
- libFuzzer documentation
- libFuzzer tutorial with examples
- More SharpFuzz samples
- OneFuzz documentation
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 releaseTip
The -rc release configuration here builds runtime in Release and libraries in Debug mode.
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.
Install the SharpFuzz command line tool:
dotnet tool install --global SharpFuzz.CommandLineBuild 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 buildRun 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.batStart fuzzing by running the local-run.bat script in the folder of the fuzzer you are interested in.
deployment/HttpHeadersFuzzer/local-run.batSee 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=5After 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-inputsThe HTML report can be opened from
.\coverage-report\html\index.htmlTo 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 _);
}
}TargetAssembliesis a list of assemblies where the tested code lives and that must be instrumented.TargetCoreLibPrefixesis the same, but for types/namespaces inSystem.Private.CoreLib.FuzzTargetis 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 inputsThis can be useful when debugging a crash.