| # 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 and |
| provide 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`, if it maintains the bug |
| - 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][#109020] and [#109129][#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 |
| ``` |
| but 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, wasting everyone's time. 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][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 is a trade-off: it 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]. |
| |
| [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/ |