| # Fuzzing |
| |
| <!-- date-check: Mar 2023 --> |
| |
| For the purposes of this guide, *fuzzing* is any testing methodology that |
| involves compiling a wide variety of programs in an attempt to uncover bugs in rustc. |
| Fuzzing is often used to find internal compiler errors (ICEs). |
| Fuzzing can be beneficial, because it can find bugs before users run into them. |
| It also provides small, self-contained programs that make the bug easier to track down. |
| However, some common mistakes can reduce the helpfulness of fuzzing and end up |
| making contributors' lives harder. |
| To maximize your positive impact on the Rust |
| project, please read this guide before reporting fuzzer-generated bugs! |
| |
| ## Guidelines |
| |
| ### In a nutshell |
| |
| *Please do:* |
| |
| - Ensure the bug is still present on the latest nightly rustc |
| - Include a reasonably minimal, standalone example along with any bug report |
| - Include all of the information requested in the bug report template |
| - Search for existing reports with the same message and query stack |
| - Format the test case with `rustfmt` |
| - Indicate that the bug was found by fuzzing |
| |
| *Please don't:* |
| |
| - Don't report lots of bugs that use internal features, including but not |
| limited to `custom_mir`, `lang_items`, `no_core`, and `rustc_attrs`. |
| - Don't seed your fuzzer with inputs that are known to crash rustc (details below). |
| |
| ### Discussion |
| |
| If you're not sure whether or not an ICE is a duplicate of one that's already |
| been reported, please go ahead and report it and link to issues you think might be related. |
| In general, ICEs on the same line but with different *query stacks* are usually distinct bugs. |
| For example, [#109020] and [#109129] had similar error messages: |
| |
| ``` |
| error: internal compiler error: compiler/rustc_middle/src/ty/normalize_erasing_regions.rs:195:90: Failed to normalize <[closure@src/main.rs:36:25: 36:28] as std::ops::FnOnce<(Emplacable<()>,)>>::Output, maybe try to call `try_normalize_erasing_regions` instead |
| ``` |
| |
| ``` |
| error: internal compiler error: compiler/rustc_middle/src/ty/normalize_erasing_regions.rs:195:90: Failed to normalize <() as Project>::Assoc, maybe try to call `try_normalize_erasing_regions` instead |
| ``` |
| |
| However, they have different query stacks: |
| ``` |
| query stack during panic: |
| #0 [fn_abi_of_instance] computing call ABI of `<[closure@src/main.rs:36:25: 36:28] as core::ops::function::FnOnce<(Emplacable<()>,)>>::call_once - shim(vtable)` |
| end of query stack |
| ``` |
| ``` |
| query stack during panic: |
| #0 [check_mod_attrs] checking attributes in top-level module |
| #1 [analysis] running analysis passes on this crate |
| end of query stack |
| ``` |
| |
| [#109020]: https://github.com/rust-lang/rust/issues/109020 |
| [#109129]: https://github.com/rust-lang/rust/issues/109129 |
| |
| ## Building a corpus |
| |
| When building a corpus, be sure to avoid collecting tests that are already known to crash rustc. |
| A fuzzer that is seeded with such tests is more likely to |
| generate bugs with the same root cause. |
| The simplest way to avoid this is to loop over each file in the corpus, see if it causes an |
| ICE, and remove it if so. |
| |
| To build a corpus, you may want to use: |
| |
| - The rustc/rust-analyzer/clippy test suites (or even source code) --- though avoid |
| tests that are already known to cause failures, which often begin with comments |
| like `//@ failure-status: 101` or `//@ known-bug: #NNN`. |
| - The already-fixed ICEs in the archived [Glacier] repository --- though |
| avoid the unfixed ones in `ices/`! |
| |
| [glacier]: https://github.com/rust-lang/glacier |
| |
| ## Extra credit |
| |
| Here are a few things you can do to help the Rust project after filing an ICE: |
| |
| - [Bisect][bisect] the bug to figure out when it was introduced. |
| If you find the regressing PR / commit, you can mark the issue with the label `S-has-bisection`. |
| If not, consider applying `E-needs-bisection` instead. |
| - Fix "distractions": problems with the test case that don't contribute to |
| triggering the ICE, such as syntax errors or borrow-checking errors |
| - Minimize the test case (see below). |
| If successful, you can label the issue with `S-has-mcve`. |
| Otherwise, you can apply `E-needs-mcve`. |
| - Add the minimal test case to the rust-lang/rust repo as a [crash test]. |
| While you're at it, consider including other "untracked" crashes in your PR. |
| Please don't forget to mark all relevant issues with `S-bug-has-test` once your PR is merged. |
| |
| See also [applying and removing labels][labeling]. |
| |
| [bisect]: https://rust-lang.github.io/cargo-bisect-rustc/ |
| [crash test]: tests/compiletest.html#crash-tests |
| [labeling]: https://forge.rust-lang.org/release/issue-triaging.html#applying-and-removing-labels |
| |
| ## Minimization |
| |
| It is helpful to carefully *minimize* the fuzzer-generated input. |
| When minimizing, be careful to preserve the original error, and avoid introducing |
| distracting problems such as syntax, type-checking, or borrow-checking errors. |
| |
| There are some tools that can help with minimization. |
| If you're not sure how to avoid introducing syntax, type-, and borrow-checking errors while using |
| these tools, post both the complete and minimized test cases. |
| Generally, |
| *syntax-aware* tools give the best results in the least amount of time. |
| [`treereduce-rust`][treereduce] and [picireny][picireny] are syntax-aware. |
| [`halfempty`][halfempty] is not, but is generally a high-quality tool. |
| |
| [halfempty]: https://github.com/googleprojectzero/halfempty |
| [picireny]: https://github.com/renatahodovan/picireny |
| [treereduce]: https://github.com/langston-barrett/treereduce |
| |
| ## Effective fuzzing |
| |
| When fuzzing rustc, you may want to avoid generating machine code, since this |
| is mostly done by LLVM. |
| Try `--emit=mir` instead. |
| |
| A variety of compiler flags can uncover different issues. |
| `-Zmir-opt-level=4` will turn on MIR optimization passes that are not run by default, potentially |
| uncovering interesting bugs. |
| `-Zvalidate-mir` can help uncover such bugs. |
| |
| If you're fuzzing a compiler you built, you may want to build it with `-C |
| target-cpu=native` or even PGO/BOLT to squeeze out a few more executions per second. |
| Of course, it's best to try multiple build configurations and see |
| what actually results in superior throughput. |
| |
| You may want to build rustc from source with debug assertions to find |
| additional bugs, though this can slow down fuzzing by |
| requiring extra work for every execution. |
| To enable debug assertions, add this to `bootstrap.toml` when compiling rustc: |
| |
| ```toml |
| rust.debug-assertions = true |
| ``` |
| |
| ICEs that require debug assertions to reproduce should be tagged |
| [`requires-debug-assertions`]. |
| |
| [`requires-debug-assertions`]: https://github.com/rust-lang/rust/labels/requires-debug-assertions |
| |
| ## Existing projects |
| |
| - [fuzz-rustc][fuzz-rustc] demonstrates how to fuzz rustc with libfuzzer |
| - [icemaker][icemaker] runs rustc and other tools on a large number of source |
| files with a variety of flags to catch ICEs |
| - [tree-splicer][tree-splicer] generates new source files by combining existing |
| ones while maintaining correct syntax |
| |
| [fuzz-rustc]: https://github.com/dwrensha/fuzz-rustc |
| [icemaker]: https://github.com/matthiaskrgr/icemaker/ |
| [tree-splicer]: https://github.com/langston-barrett/tree-splicer/ |