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 following arguments:
./build.cmd clr+libs+packs+host -rc Checked -c Debugand install the SharpFuzz command line tool:
dotnet tool install --global SharpFuzz.CommandLineTip
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 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 inputsThe 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).
cd src/libraries/Fuzzing/DotnetFuzzing
dotnet publish -o publish && publish/DotnetFuzzing.exe prepare-onefuzz deploymentYou can now start 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=5To 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.