From 008ef34e787feb64f87f10ac87be75e11525dc20 Mon Sep 17 00:00:00 2001 From: cwshugg Date: Mon, 4 Nov 2024 19:33:53 +0000 Subject: [PATCH 1/2] added documentation for fuzzing Windows and Windows DLLs --- src/SUMMARY.md | 3 + src/cargo-fuzz/setup.md | 5 +- src/cargo-fuzz/windows.md | 8 + src/cargo-fuzz/windows/dll-fuzzing.md | 223 ++++++++++++++++++++++++++ src/cargo-fuzz/windows/setup.md | 40 +++++ 5 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 src/cargo-fuzz/windows.md create mode 100644 src/cargo-fuzz/windows/dll-fuzzing.md create mode 100644 src/cargo-fuzz/windows/setup.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 34b4ab0..a6f7f78 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -9,6 +9,9 @@ * [Structure-Aware Fuzzing](./cargo-fuzz/structure-aware-fuzzing.md) * [Coverage](./cargo-fuzz/coverage.md) * [Targets](./cargo-fuzz/targets.md) + * [Fuzzing on Windows](./cargo-fuzz/windows.md) + * [Setup](./cargo-fuzz/windows/setup.md) + * [Fuzzing DLLs](./cargo-fuzz/windows/dll-fuzzing.md) * [Fuzzing with afl.rs](./afl.md) * [Setup](./afl/setup.md) * [Tutorial](./afl/tutorial.md) diff --git a/src/cargo-fuzz/setup.md b/src/cargo-fuzz/setup.md index 00e37f2..ba25499 100644 --- a/src/cargo-fuzz/setup.md +++ b/src/cargo-fuzz/setup.md @@ -2,7 +2,7 @@ ## Requirements -libFuzzer needs LLVM sanitizer support, so this only works on x86-64 Linux, x86-64 macOS and Apple-Silicon (aarch64) macOS for now. Requires a C++ compiler with C++11 support. Rust provides multiple compilers. This project requires the nightly compiler since it uses the `-Z` compiler flag to provide address sanitization. Assuming you used [rustup][rustup] to install Rust, you can check your default compiler with: +libFuzzer needs LLVM sanitizer support; this works on x86-64 Linux, x86-64 macOS and Apple-Silicon (aarch64) macOS, and Windows (thanks to the [MSVC AddressSanitizer][msvc-asan]). Requires a C++ compiler with C++11 support. Rust provides multiple compilers. This project requires the nightly compiler since it uses the `-Z` compiler flag to provide address sanitization. Assuming you used [rustup][rustup] to install Rust, you can check your default compiler with: ```shell $ rustup default @@ -29,4 +29,5 @@ cargo install cargo-fuzz cargo install --force cargo-fuzz ``` -[rustup]: https://github.com/rust-lang/rustup \ No newline at end of file +[rustup]: https://github.com/rust-lang/rustup +[msvc-asan]: https://learn.microsoft.com/en-us/cpp/sanitizers/asan diff --git a/src/cargo-fuzz/windows.md b/src/cargo-fuzz/windows.md new file mode 100644 index 0000000..7c3817f --- /dev/null +++ b/src/cargo-fuzz/windows.md @@ -0,0 +1,8 @@ +# Fuzzing on Windows + +[cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) can be used to fuzz +Windows programs, thanks to the [MSVC +AddressSanitizer](https://learn.microsoft.com/en-us/cpp/sanitizers/asan). Read +on to learn how to set up your Windows and PowerShell environment for building +and fuzzing on Windows. + diff --git a/src/cargo-fuzz/windows/dll-fuzzing.md b/src/cargo-fuzz/windows/dll-fuzzing.md new file mode 100644 index 0000000..2ede846 --- /dev/null +++ b/src/cargo-fuzz/windows/dll-fuzzing.md @@ -0,0 +1,223 @@ +# Fuzzing Windows DLLs + +On Windows systems, shared libraries are called [**Dynamic Link +Libraries**](https://learn.microsoft.com/en-us/troubleshoot/windows-client/setup-upgrade-and-drivers/dynamic-link-library) +(**DLLs**). Code can be compiled into a `.dll` file, which is then loaded in at +run-time by other executables on the system. + +You might find yourself wanting to fuzz a Windows DLL. Like any other piece of +software, a shared library would benefit from undergoing fuzzing. Currently, +fuzzing a Windows DLL is possible, but *slightly* trickier. Read on! + +## How do I Build and Fuzz a DLL? + +Follow these steps to build your DLL for fuzzing and build your fuzzing targets +to invoke it. + +### Set up your Fuzzing `Cargo.toml` + +Add your DLL's Cargo project to your fuzzing `Cargo.toml` as an optional +dependency: + +```toml +[dependencies] +# ... +your_dll = { path = "..", optional = true } +# ... +``` + +Add a feature to your `Cargo.toml` that requires your DLL as a dependency. We +want the DLL to be built *only* when this feature is enabled: + +```toml +[features] +# ... +build_your_dll = [ + "dep:your_dll" +] +# ... +``` + +Finally, create a fuzzing target in your `Cargo.toml` that requires this +feature. This will be a "dummy" fuzzing target whose sole purpose is to build +your DLL. It won't actually do any fuzzing; it's merely a way to have +cargo-fuzz build your DLL with AddressSanitizer (and other) instrumentation. + +```toml +[[bin]] +name = "build_your_dll" +path = "fuzz_targets/build_your_dll.rs" +required-features = ["build_your_dll"] +test = false +doc = false +bench = false +``` + +### Create the "Dummy" Fuzzing Target + +Next, you need to create the source code for this "dummy" fuzzing target +(`fuzz_targets/build_your_dll.rs`). At its simplest, all you need to do is +create a simple `main` function: + +```rust +pub fn main() +{ + println!("DLL build complete!"); +} +``` + +This "dummy" target will have its main function executed *after* the DLL build +has completed, so if you'd like, you can add extra code here to perform any +post-build installation or setup. (For example, perhaps you need to copy the +built DLL to somewhere else on the system, in order for the fuzzing targets to +find it.) + +### Create your Fuzzing Targets + +After that, it's cargo-fuzz business as usual: create your fuzzing targets in +`Cargo.toml`, and have them load and invoke your DLL: + +```toml +[[bin]] +name = "fuzz_your_dll_1" +path = "fuzz_targets/fuzz_your_dll_1.rs" +test = false +doc = false +bench = false +``` + +### Build the DLL and Run + +To build the DLL, then run a fuzzing target, there are two separate commands +you need to invoke: + +```powershell +# Build the DLL with your "dummy" target +cargo fuzz run --features=build_your_dll --no-include-main-msvc --strip-dead-code build_your_dll +``` + +(See the ["Technical Details"](#Technical-Details) for more information on why +these options are needed.) + +```powershell +# Run your fuzzing target, now that your DLL is built +cargo fuzz run fuzz_your_dll_1 +``` + +## Technical Details + +
+ +(Why do we have to fuzz DLLs this way? Click here to see some details.) + + +Code that is fuzzed through cargo-fuzz must be compiled with extra +instrumentation inserted. The binary that is produced behaves normally, but +executes additional code that cargo-fuzz (which uses +[LibFuzzer](https://llvm.org/docs/LibFuzzer.html) under the hood) can use to +recieve feedback about how the target program behaved when given inputs from the +fuzzer. In this way, a fuzzing "feedback loop" is established, and the fuzzer +can slowly generate more "interesting" inputs that create new behavior in the +target program. + +In our case, the target program is a Windows DLL. Because it's a DLL +(shared library), it must be built and instrumented as a completely separate +binary (a `.dll` file) from any fuzzing target executable (`.exe`) we've +developed to test it. Your fuzzing target programs +(`..../fuzz/fuzz_target/*.rs`) are calling functions from this DLL, but the +actual loading of those functions into the same process will occur at run-time. + +So, there are two steps that need to be done when building (hence the two +separate `cargo fuzz run ...` commands listed above): + +1. Build the DLL and install it. +2. Build the fuzzing targets. + +### MSVC and LibFuzzer's `main` Function + +On Windows, Rust uses the [MSVC compiler and +linker](https://learn.microsoft.com/en-us/cpp/build/reference/compiling-a-c-cpp-program) +to build. The cargo-fuzz fuzzing targets do not implement a `main` function; +instead, they use LibFuzzer's built-in `main` function. (This function is what +actually starts up the fuzzer. The fuzzer then invokes the `fuzz_target!()` +macro function defined in each fuzzing target.) The MSVC linker does not +seem to recognize the LibFuzzer `main` function, and thus cannot build the +fuzzing targets without a little help. + +To fix the problem, cargo-fuzz code adds +the `/include:main` linker argument to the build arguments passed to `cargo +build` when it detects systems that are building with MSVC. This arguments +forces the inclusion of an external `main` symbol in the executables produced by +MSVC. (See more on the `/include` argument +[here](https://learn.microsoft.com/en-us/cpp/build/reference/include-force-symbol-references).) +This allows the fuzzing targets to build. + +### Adding `/include:main` breaks DLL Linking + +But hang on a second! DLLs by nature are shared libraries, and thus should not +have any references to a `main` function. It's the job of the executable that +loads a DLL into worry about `main`. So, if we attempt to build a DLL using +`cargo fuzz build`, it'll add the `/include:main`, and we'll get a linker error: + +``` +LINK : error LNK2001: unresolved external symbol main +C:\....\my_shared_library.dll : fatal error LNK1120: 1 unresolved externals +``` + +To avoid this, we use the `--no-include-main-msvc` argument, which allows us to +control whether or not `/include:main` is added to the MSVC linker arguments. + +### But removing `/include:main` breaks Fuzzing Target Linking + +However... we need `/include:main` to build the fuzzing target executables. This +puts us at a bit of an impasse: + +* If we add `/include:main`, the fuzzing targets will build, but the DLL will + not. +* If we remove `/include:main`, the DLL will build, but the fuzzing targets will + not. + +### Solution: Two Separate Builds + +To solve this, we need to invoke `cargo fuzz ...` twice: once to build the DLL +(*without* `/include:main`), and another time to build the fuzzing targets +(*with* `/include:main`). In order to build the DLL using cargo-fuzz (which we +want to do, because it builds using all the relevant LLVM coverage and +AddressSanitizer compiler options), we implement a small "dummy" fuzzing target +that provides its own `main` function. + +This "dummy" target does not implement a `fuzz_target!()` macro function (and +thus, no actual fuzzing occurs), but it acts as a vehicle for us to build the +Windows DLL for fuzzing. Plus, you can add any extra code to this "dummy" target +to help install your newly-built DLL in the correct location on your Windows +system. + +### Why Use `--strip-dead-code`? + +By default, cargo-fuzz invokes rustc with the `-Clink-dead-code` argument. +This, as described +[here](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-dead-code), +controls whether or not the linker is instructed to keep dead code. "Dead code" +refers to functions/symbols that are provided by some dependency (such as a +DLL) but aren't ever referenced/used by the program that's importing code from +the dependency. This can be useful in some cases, but harmful in others. + +In the case of the certain DLLs, it may be harmful. By building with +`-Clink-dead-code`, references to unused functions/symbols within various +Windows DLLs your target DLL is dependent on would be included in the resulting +binary when you build it with cargo-fuzz. + +For example: in [windows-rs](https://github.com/microsoft/windows-rs), the +Cryptography sub-crate (`windows::Win32::Security::Cryptography`) includes +symbols from `infocardapi.dll`). This DLL appears to no longer be supported, or +even installed on Windows. If `-Clink-dead-code` were to cause these symbols to +be included in your DLL, loading will fail at run-time when, inevitably, those +symbol references can't be found, since `infocardapi.dll` is nowhere to be +found on the system. (Your fuzzing target program will fail with +`STATUS_DLL_NOT_FOUND`.) + +This issue can be fixed by adding `--strip-dead-code` to your cargo-fuzz +command, which removes the usage of `-Clink-dead-code` when building. + +
+ diff --git a/src/cargo-fuzz/windows/setup.md b/src/cargo-fuzz/windows/setup.md new file mode 100644 index 0000000..09d4e92 --- /dev/null +++ b/src/cargo-fuzz/windows/setup.md @@ -0,0 +1,40 @@ +# Windows Setup + +It's possible to use cargo-fuzz to fuzz Rust code on Windows. This guide aims +to shed some light on how you can get cargo-fuzz up and running on a Windows +system. + +## 1. Install Visual Studio + +Make sure you have Visual Studio installed; there are a number of features +that need to be installed alongside it in order for the fuzzing code to build. +Follow [this +guide](https://learn.microsoft.com/en-us/visualstudio/install/install-visual-studio) +to install the "Visual Studio Installer". Use it to make sure you have the +following individual components installed: + +* MSVC v143 - VS 2022 C++ x64/x86 build tools + * (This was the latest at the time of writing - you may install a newer + version if you're reading this at a later point in time!) +* C++ AddressSanitizer + +## 2. Set up PowerShell + +Certain directories must be on the PowerShell system `$env:PATH` in order for +builds to succeed and for certain cargo-fuzz commands to work. The `Developer +PowerShell for VS 2022` and/or `x64 Native Tools Command Prompt for VS 2022` +may have these directories already on the path. If they don't, or you are using +a different PowerShell, make sure these directories are added to the shell's +`$env:PATH`: + +* `C:\Program Files\Microsoft Visual Studio\Community\VC\Tools\MSVC\\bin\Hostx86\64` + * Where `` is the MSVC version you have installed. +* (Optional) `C:\Program Files (x86)\Windows Kits\10\Debuggers\x64` + * Add this if you want to use the [Windows + Debugger](https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/windbg-overview) + to debug your fuzzing targets. + +These paths may very slightly on your machine, but the main idea is that your +shell needs to be able to find the MSVC-based AddressSanitizer DLL as well as +the other MSVC-related binaries. + From bf6bd398052eda269e539d3961bdf703ec4ed762 Mon Sep 17 00:00:00 2001 From: cwshugg Date: Thu, 7 Nov 2024 20:19:00 +0000 Subject: [PATCH 2/2] fixed minor mdbook syntax issues --- src/cargo-fuzz/windows/dll-fuzzing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo-fuzz/windows/dll-fuzzing.md b/src/cargo-fuzz/windows/dll-fuzzing.md index 2ede846..a8b62d4 100644 --- a/src/cargo-fuzz/windows/dll-fuzzing.md +++ b/src/cargo-fuzz/windows/dll-fuzzing.md @@ -159,9 +159,9 @@ have any references to a `main` function. It's the job of the executable that loads a DLL into worry about `main`. So, if we attempt to build a DLL using `cargo fuzz build`, it'll add the `/include:main`, and we'll get a linker error: -``` +```txt LINK : error LNK2001: unresolved external symbol main -C:\....\my_shared_library.dll : fatal error LNK1120: 1 unresolved externals +C:/..../my_shared_library.dll : fatal error LNK1120: 1 unresolved externals ``` To avoid this, we use the `--no-include-main-msvc` argument, which allows us to