-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a flag to toggle -Zbuild-std, and default to using it #292
Conversation
I'm not convinced that we want to do this by default, mainly because of the compile time overhead rather than run time overhead. I think having an option makes a lot of sense, though. |
I agree that the compile time overhead is concerning, but Building an
ASan
-Zbuild-std, no sanitizers
Building a
ASan
-Zbuild-std, no sanitizers
The checks added by ASan have not found all that many bugs in the Rust ecosystem. I suspect if/when the PR I linked lands, it will find more UB than the ASan option. So I think you're left making an awkward argument about the value of the UB that ASan may detect to continue to justify it as a default. I am also concerned about codebases supposedly having fuzzing but missing bugs because they didn't turn on some option. We already run into this with Miri. If you say "my code passes Miri" there are a growing number of people who will say "Well did you run |
The sanitizer(s) are (or were in the past, at least) required for fuzzing to work at all, as the instrumentation from sanitizers was how libfuzzer would gather information from the program execution. |
When I say "no sanitizers" I mean Looks like coverage doesn't work, because of rust-lang/wg-cargo-std-aware#63 sigh |
This is not an argument to add even more compile time overhead. If anything it motivates disabling ASan by default (which I would be open to! but its default enabling predates my time here, so I'd want to get input on that from @Manishearth and @nagisa). |
iirc we enabled ASan because that was the only way at the time to get sanitizer tooling |
My apologies for the confusion. I agree. I meant to point out that I think the cost and benefits of |
Okay so my take away thus far in the discussion is that we should:
Does that sound reasonable? |
I think so yeah. We should make sure it's easy to turn on the sanitizers and document both these options prominently |
Perhaps raise a warning when asan isn't enabled for a couple releases so people know what's going on? |
I'd be happy to see Personally, I think extra 30 seconds to build the standard library are probably inconsequential when the fuzz target will be running for a long time. In my eyes, most of the resources in fuzzing will be spent on actually running the fuzz target itself rather than compiling them, and so optimizing for the chance to detect an issue might make more sense? |
This also works for me; a slight worry i have is that the instrumented stdlib will slow down fuzzing itself, but I suspect it's not too bad. |
🤦 I'm still not opposed to turning off ASan by default with a warning, but should there be a second way to turn on |
In general I think we add generally useful rustc options as separate flags so they're more prominent in |
|
Assuming that
Is true and that enabling these flags would indeed find more issues then I don't really see a reason not to? Though, I am also assuming that it building the std is visible to the user and that sooner or later a better way to get these checks is available. The reasoning for this is simple: It sounds like those 30 extra seconds actually save time in the long run as it means that people don't have to run it a second time because they forgot to enable them. Also, if you go out of your way to fuzz your crate, you do it to find bugs. And though options are nice to sacrifice some detection in favor of speed, I think that in general people care more about the amount of bugs that get found by fuzzing then how long the fuzzing takes. So, TL;DR: I vote for enabling both Asan and build-std by default but make it clear to the user that both are enabled and make it easy for the user to find out what their benefits and downsides are. |
For both fuzzing and Miri, this seems like a situation where it might be good to have "profiles" to pick from, in the same sense that Cargo offers profiles of compiler options (dev/release/test/bench). These profiles could provide different sets of “good defaults” with documented intended use cases, so that a user can choose one and get good-enough results, while still allowing customization by additional flags. This would improve on specifying individual flags because:
Some possible use-cases/profiles:
Choosing a profile could be mandatory, so that there is no need to consider what the defaults in the absence of a profile would be. |
(for transparency, i was linked to this thread to leave a comment by saethlin) One bug that shows up a fair bit in binary parsing that asan does catch is OOM. With asan, there's a nice backtrace leading you to the place you allocated the memory. Without, it either ignores it, or presumably just aborts on OOM. So even if asan doesn't catch any safety issues, catching OOM issues (where people read a u32 or u64 length from some file, and then do Though I generally do run with I generally don't go out of my way to -Zbuild-std, but I've tried it now with the serde_json fuzzer and the build time is definitely what I'd call perfectly reasonable for a fuzz target, it's something I'm fine with being the default, personally. I'd definitely appreciate some way to catch internal invariants being broken, especially since you can't fuzz with miri (imagine the executions per second :fear:), at least some debug assertions are good. From the benchmarks above, -Zbuild-std looks negligible in terms of perf to compile time, so I think making it on by default is good. It does look to have actually a pretty significant hit to executions per second (13k or so with serde with -Zbuild-std, 25k or so without), which is what I'd be more concerned about though. That's not too bad, but you'd definitely want to turn that off if you're fuzzing safe code where you're not looking to hit the debug assertions. So I'm going with "on by default but make it easy to turn off" As for asan, it's much heavier a compile time hit and runtime hit (without -Zbuild-std, This is already easy to turn off, so I'm inclined to also say leave it on by default, making the defaults "slow but accurate" in terms of how many bugs will be caught. You'd need to leave the fuzzer on for maybe 2-8x longer than you would otherwise, but I'm personally fine with just teaching users to "pass |
I agree with this. No need to figure out what configuration people would most likely be using if there are no defaults. It also gives a perfect opportunity to teach the user about what options are available. |
According to this Mozilla blog post, Thread Sanitizer will not detect some of the races unless I made
Agreed. Fuzzing, just like cryptography, is a field where you get no feedback when you're doing something horribly wrong. It seems like everything is working, when in reality it's not. So just like cryptography, fuzzing should default to the most robust option that makes it really hard to mess up. Any optimizations should be opt-in for people who really know what they're doing. I am reminded of CryptoCat developers infamously setting "1" as the RSA exponent because that was much faster than the alternatives, which led to the data not being encrypted at all (1 to any power is always 1). I really like |
I am commenting here because the motivating PR with a bunch of debug assertions in I agree very strongly with @Shnatsel's point, and I think that by default we should do the most checks possible. If it were up to me, we'd have ASan + build-std + debug_assertions on by default. If people want to turn off debug assertions or build-std, they can turn off debug assertions in I sort of agree with @kpreid's suggestion, in theory. I think that usage of cargo-fuzz is easily categorized but when it comes down to picking names for the profiles and deciding exactly what will be in them I'm at a loss. ASan, TSan, and MSan all conflict with each other, so we need either at least 4 profiles, or some mixture of profile flags and sanitizer flags, at which point we don't really have fewer flags than stipulating all the individual options. And thinking back, I've talked to a few users of cargo-fuzz who don't want to turn off ASan, even if the only unsafe code in their build is in the standard library. Whether or not this decision is well-informed is somewhat besides the point; I think some people would be bothered to learn it had been toggled off for them. In terms of rapid iteration speed, |
Ah, when I wrote that suggestion I wasn't aware there were conflicts. The first idea I think of to address that is: for an "everything" profile, why not compile the fuzz target with the incompatible sanitizers separately, and run all of them? Support for running more than one fuzz target binary (whether in parallel or interleaved) would also benefit users with several fuzz targets who want to throw some CPU time at checking everything (after making an infrastructure change that potential affects multiple entry-points, or in CI). This is a feature I'd like to use, even independent of any question of flag-combinations. |
That sounds awesome and out of scope for this PR 😅 |
I changed this PR back to default to turning on |
@saethlin Address Sanitizer already detects both out-of-bounds indexing and improper use of |
Address Sanitizer detects reads and writes which are out-of-bounds of the allocation, which is a significantly more permissive check than what the standard library requires. For example, this program executes UB which is caught by the standard library debug assertions but not caught by Address Sanitizer: fn main() {
let v = vec![1, 2, 3];
let sli = &v[..1];
let _ouch: &i32 = unsafe { sli.get_unchecked(1) };
} And it is still not caught if we access through the reference. Address Sanitizer also does not catch misuse of fn main() {
let mut v = vec![1, 2, 3];
let ptr = v.as_mut_ptr();
unsafe { core::ptr::copy_nonoverlapping(ptr, ptr, 1) };
} |
* based on [this PR](rust-fuzz/cargo-fuzz#292) to `cargo-fuzz` by @saethlin * based on [cargo-careful](https://github.com/RalfJung/cargo-careful) by @RalfJung
Superseded by #339 |
By default, all the debug assertions in the standard library are compiled out, and the only way to get them back is
-Zbuild-std
.Even generic debug assertions can catch code that passes the sanitizers. For example, in this PR I'm trying to add more and I found a few bugs in the compiler by doing so: rust-lang/rust#92686
Having these assertions on in general is also a very good idea, because even if the behavior would be caught eventually, catching it at the source is an aid to debugging. Example here: https://reddit.com/r/rust/comments/t87itu/how_can_i_debugreport_possible_compiler_bugs/
Turning on
-Zbuild-std
with debug assertions enabled introduces a performance regression. I'm trying to squint at a bunch of numbers over here, but things keep shifting around. Maybe I'm just bad at this. My best guess is that turning on-Zbuild-std
is a ~20% regression. Still much better than just turning on ASan, which is a ~60% regression.I'm also a big fan of having these on by default because these debug assertions work everywhere and with all codebases, as opposed to tools like ASan which doesn't necessarily.