-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
[WIP] Sanitizers support. #31605
[WIP] Sanitizers support. #31605
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Aatch (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
@alexcrichton Another case for additional standard library bins, and custom built std. |
This is so good. lgtm. r? @alexcrichton |
This is also quite exciting for me to see as well, thanks for taking this on @tmiasko! I've long wanted to see these for Rust :) |
@@ -558,9 +584,10 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, | |||
"explicitly enable the cfg(debug_assertions) directive"), | |||
inline_threshold: Option<usize> = (None, parse_opt_uint, | |||
"set the inlining threshold for"), | |||
sanitize: Option<Sanitize> = (None, parse_sanitize, | |||
"choose the sanitizer to use"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, I think it's fair to say that this is a relatively unstable option, so could this be behind the -Z
flags instead of -C
? Once we get more experience with it we may be able to promote it to -C
Reading this all it looks pretty good to me, my only point of hesitation might be the modifications to the linker backend. There's a few pieces of logic which make me uneasy:
All of these are currently handled in the compiler, but I could also imagine a crate that looks like: #![crate_name = "asan"]
#![cfg(sanitize = "address")] // crate is a noop without address sanitizer
extern crate alloc_system; // this only works if we use the system allocator
#[link(name = "rustc_asan", kind = "static")] // runtime support library
#[link(name = "pthread")] // native deps
#[link(name = "c")]
extern {}
// Any Rust-provided support necessary for this as well
#[no_mangle]
pub extern fn asan_runtime_foo() {} Basically, using a crate for this would mean that all the linker pieces are taken care of, the In terms of usage, we could detect Now all that being said, I could also see the argument that the compiler is injecting calls to this runtime support, so it should be the one providing this code in a more intrusive manner (similarly to how compiler-rt works today). Curious what you think, though? Does an organizational unit of a crate make sense? |
Oh, some other thoughts:
|
cc @japaric if you're interested in moving |
Regarding question of using separate crate, I think that would make sense. At |
Leak sanitizer does not require any instrumentation at all. For thread Diagnosing mixing of instrumented code and non-instrumented one would be great! |
Using a crate to handle the linker configuration is a cool idea, but it does require more things to configure - the compiler doesn't control everything, now you've got to link to something too. In the end it needs to be simple to activate both the compiler support and library support at once, on demand. @alexcrichton how would you see people activating the sanitizers? |
I'd probably see one of two routes to be taken here:
@tmiasko could you elaborate on the |
The general problem with as-needed is described here: Example code uses clock_gettime which is defined in librt. This function is Currently I can't reproduce this on my system, because glibc clock_* functions I think we can try with crates, because this seems a little less invasive on |
I rebased the code and addressed all comments but those regarding using crates. Using crates turned out to be a little more involved than I thought initially. Alternatively injecting two kinds of dependencies should be done separately:
|
☔ The latest upstream changes (presumably #31474) made this pull request unmergeable. Please resolve the merge conflicts. |
@@ -636,6 +636,23 @@ pub fn run_passes(sess: &Session, | |||
let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); | |||
let mut metadata_config = ModuleConfig::new(tm, vec!()); | |||
|
|||
sess.opts.debugging_opts.sanitize.map(|s| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In terms of style, this is typically expressed as:
if let Some(s) = sess.oopts.debuggings_opts.sanitize {
// ...
}
Thanks @tmiasko!
This is assuming that we compile It may also just be possible to ensure that the sanitizer crate shows up at the end of the topological ordering to ensure it shows up at the end. For the |
Yes, I was assuming that alloc_system would be compiled with sanitizer support But we have to keep in mind that alloc_system is merely an interface to system The With respect to this PR, do expect me to make some additional changes (apart |
I guess I'm a little unclear on what the next steps for the PR is if it lands. Do we want to start moving towards stabilizing the To me the best way forward would be to land support in the compiler but through a mechanism that's as minimally invasive as possible. Along those lines I'd expect this to modify the pass manager we give to LLVM with some additional passes, but not much else. The changes to allocation crates as well as the addition of runtime dependencies/linker flags seems... unfortunate? Overall I think that it'd probably be best to figure out a story for sanitizers in general before landing this. I can definitely see us starting to ship support built-in, but there may be bunch of other factors we need to take into account before doing that. |
Ok, I am closing this for now. If at some point in a future cargo will support |
I was recently working on support for thread sanitizer and leak sanitizer in
rustc. This is still work in progress, so all kinds of comments, suggestions
and help is welcomed.
Leak sanitizer intercepts malloc and friends, records allocations (including
program backtrace at allocation time) and at program exit reports all leaks (in
atexit callback). Enabling it basically requires linking in lsan runtime
library when building final executable.
Supporting thread sanitizer is a little bit more complicated. Thread sanitizer
combines function interception with additional LLVM pass that instruments
atomic intrinsics, memory intrinsics (memcpy, ...), and memory operations,
replacing them with calls to tsan runtime. Full instrumentation is performed
only for functions with LLVM SanitizeThread attribute.
Address sanitizer and memory sanitizers are also included, but I am yet to
analze how clang and llvm supports them, so this could be quite broken on
anything but trivial programs.
Current state and things that are still left to be done:
using rustbuild.
sanitizers tests when running on unsupported platform or if sanitizers have
not been built at all. This blocked me from including tests dedicated for
sanitizers.
not work with liballoc_jemalloc (i.e., sanitizer is not aware of allocations
done through jemalloc). I tried to alleviate this problem by making
liballoc_system default allocator when used with sanitizers.
additional call on function entry and function exit to maintain so called
shadow stack. During unwinding exit callback function is not called which
leaves shadow stack inconsistent, or could even lead to shadow stack
overflow. Though, it seems that it could be supported in future:
TSAN does not support C++ exceptions? google/sanitizers#485.
current Arc implementation, and reports false positives. See following for
more details:
https://groups.google.com/d/topic/thread-sanitizer/B4i9EMQ4BQE/discussion
https://groups.google.com/d/topic/thread-sanitizer/dZ3QiQE49ns/discussion
Replacing fences in Arc by AcqRel on fetch_sub is one possible workaround
(this is for example implementation used in boost:
sp_counted_base_std_atomic.hpp, sp_counted_base_clang.hpp).
compiled with the same sanitizer flags. Unfortunately it is quite easy to get
this wrong.
Two small examples how this works. First leak sanitizer:
And thread sanitizer: