SharpFuzz provides two key capabilities:
- A CLI tool that rewrites assemblies to enable coverage feedback
- C# APIs for authoring fuzzing harnesses
SharpFuzz was originally written to support fuzzing via AFL.
The libfuzzer-dotnet sibling project
enables using a SharpFuzz harness with LibFuzzer as the fuzzing engine,
on either Linux or Windows.
Note: From a LibFuzzer perspective,
libfuzzer-dotnetis an atypical repurposing of the LibFuzzer engine and APIs. A normal LibFuzzer binary links in both some fixed, user-provided code under test and a compiler-provided fuzzing engine and runtime. The result is one LibFuzzer binary per fuzzing target. In contrast,libfuzzer-dotnetis a single LibFuzzer that has a special--target_pathparameter. This is used to specify the path to a self-contained fuzzing harness, which is then spawned and managed with parent-child IPC. In this way,libfuzzer-dotnetuses LibFuzzer to create a special stand-alone fuzzing executor for CLR code.
- Clang >= 14.0.0 (for now)
- .NET 6.0 SDK
SharpFuzzlibrary >= 2.0sharpfuzzCLI tool >= 1.0
Using the dotnet CLI, invoke:
dotnet tool install --global SharpFuzz.CommandLineDownload the source for the Windows version of libfuzzer-dotnet.
# Snapshot that matches release of SharpFuzz 2.0.
$src = "https://raw.githubusercontent.com/Metalnem/libfuzzer-dotnet/55d84f84b3540c864371e855c2a5ecb728865d97/libfuzzer-dotnet-windows.cc"
iwr $src -o libfuzzer-dotnet-windows.ccCompile the (parameterized) fuzzer.
clang -g -O2 -fsanitize=fuzzer .\libfuzzer-dotnet-windows.cc -o libfuzzer-dotnet.exeYou should now have an executable named libfuzzer-dotnet.exe.
This is a special, parameterized LibFuzzer which
requires a non-standard --target_path argument.
A libfuzzer-dotnet binary only needs to be created once per native platform.
It does not need to be recompiled for each CLR target.
In the next step, we will create this fuzzing target by publishing a self-contained native executable (and supporting directory) that links our fuzzing target.
Our example code under test can be found in the Example project.
The accompanying fuzzing harness can be found in the ExampleFuzzer project.
Note, these do not need to be distinct projects.
In this case, they are, and ExampleFuzzer depends on the Example project.
We need to build and publish ExampleFuzzer as a self-contained executable deployment.
This will provide a platform-specific executable that bundles a .NET runtime.
After that, we will instrument the DLL for our actual target code.
From the repo root, invoke:
mkdir out
dotnet publish ExampleFuzzer -c Release -o out --sc -r win10-x64Now instrument the DLL that contains our fuzzing target code. This lets the LibFuzzer engine detect when randomly-generated test cases uncover new code, and significantly improves fuzzing efficacy.
sharpfuzz out/Example.dll
Now, you should be able to run the fuzzer like so:
# Create a directory to save inputs that increased code coverage.
mkdir corpus
# Run the parameterized fuzzer with our self-contained target.
./libfuzzer-dotnet --target_path=out/ExampleFuzzer.exe corpusYou can reproduce discovered crashes with the LibFuzzer harness:
./libfuzzer-dotnet.exe --target_path=out/ExampleFuzzer.exe ./crash.txtOr without, using the self-contained assembly directly:
./out/ExampleFuzzer.exe crash.txt