-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from cwshugg/windows_fuzzing
Documentation for Windows Fuzzing
- Loading branch information
Showing
5 changed files
with
277 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
||
<details> | ||
<summary> | ||
(Why do we have to fuzz DLLs this way? Click here to see some details.) | ||
</summary> | ||
|
||
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: | ||
|
||
```txt | ||
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. | ||
|
||
</details> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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\<VERSION_NUMBER>\bin\Hostx86\64` | ||
* Where `<VERSION_NUMBER>` 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. | ||
|