Skip to content

build-std compatible sanitizer support #65241

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

Merged
merged 5 commits into from
Jan 11, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 0 additions & 48 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -3348,17 +3348,6 @@ dependencies = [
"smallvec 1.0.0",
]

[[package]]
name = "rustc_asan"
version = "0.0.0"
dependencies = [
"alloc",
"build_helper",
"cmake",
"compiler_builtins",
"core",
]

[[package]]
name = "rustc_ast_lowering"
version = "0.0.0"
@@ -3680,17 +3669,6 @@ dependencies = [
"libc",
]

[[package]]
name = "rustc_lsan"
version = "0.0.0"
dependencies = [
"alloc",
"build_helper",
"cmake",
"compiler_builtins",
"core",
]

[[package]]
name = "rustc_macros"
version = "0.1.0"
@@ -3752,17 +3730,6 @@ dependencies = [
"syntax",
]

[[package]]
name = "rustc_msan"
version = "0.0.0"
dependencies = [
"alloc",
"build_helper",
"cmake",
"compiler_builtins",
"core",
]

[[package]]
name = "rustc_parse"
version = "0.0.0"
@@ -3935,17 +3902,6 @@ dependencies = [
"syntax",
]

[[package]]
name = "rustc_tsan"
version = "0.0.0"
dependencies = [
"alloc",
"build_helper",
"cmake",
"compiler_builtins",
"core",
]

[[package]]
name = "rustc_typeck"
version = "0.0.0"
@@ -4307,10 +4263,6 @@ dependencies = [
"panic_unwind",
"profiler_builtins",
"rand 0.7.0",
"rustc_asan",
"rustc_lsan",
"rustc_msan",
"rustc_tsan",
"unwind",
"wasi 0.9.0+wasi-snapshot-preview1",
]
1 change: 1 addition & 0 deletions src/bootstrap/builder.rs
Original file line number Diff line number Diff line change
@@ -343,6 +343,7 @@ impl<'a> Builder<'a> {
tool::Rustdoc,
tool::Clippy,
native::Llvm,
native::Sanitizers,
tool::Rustfmt,
tool::Miri,
native::Lld
2 changes: 1 addition & 1 deletion src/bootstrap/check.rs
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ impl Step for Std {
let compiler = builder.compiler(0, builder.config.build);

let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind));
std_cargo(builder, &compiler, target, &mut cargo);
std_cargo(builder, target, &mut cargo);

builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target));
run_cargo(
80 changes: 40 additions & 40 deletions src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ impl Step for Std {
target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter());

let mut cargo = builder.cargo(compiler, Mode::Std, target, "build");
std_cargo(builder, &compiler, target, &mut cargo);
std_cargo(builder, target, &mut cargo);

builder.info(&format!(
"Building stage{} std artifacts ({} -> {})",
@@ -153,17 +153,18 @@ fn copy_third_party_objects(
copy_and_stamp(Path::new(&src), "libunwind.a");
}

if builder.config.sanitizers && compiler.stage != 0 {
// The sanitizers are only copied in stage1 or above,
// to avoid creating dependency on LLVM.
target_deps.extend(copy_sanitizers(builder, &compiler, target));
}

target_deps
}

/// Configure cargo to compile the standard library, adding appropriate env vars
/// and such.
pub fn std_cargo(
builder: &Builder<'_>,
compiler: &Compiler,
target: Interned<String>,
cargo: &mut Cargo,
) {
pub fn std_cargo(builder: &Builder<'_>, target: Interned<String>, cargo: &mut Cargo) {
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
}
@@ -206,19 +207,6 @@ pub fn std_cargo(
let mut features = builder.std_features();
features.push_str(&compiler_builtins_c_feature);

if compiler.stage != 0 && builder.config.sanitizers {
// This variable is used by the sanitizer runtime crates, e.g.
// rustc_lsan, to build the sanitizer runtime from C code
// When this variable is missing, those crates won't compile the C code,
// so we don't set this variable during stage0 where llvm-config is
// missing
// We also only build the runtimes when --enable-sanitizers (or its
// config.toml equivalent) is used
let llvm_config = builder.ensure(native::Llvm { target: builder.config.build });
cargo.env("LLVM_CONFIG", llvm_config);
cargo.env("RUSTC_BUILD_SANITIZERS", "1");
}

cargo
.arg("--features")
.arg(features)
@@ -276,31 +264,43 @@ impl Step for StdLink {
let libdir = builder.sysroot_libdir(target_compiler, target);
let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));

if builder.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
// The sanitizers are only built in stage1 or above, so the dylibs will
// be missing in stage0 and causes panic. See the `std()` function above
// for reason why the sanitizers are not built in stage0.
copy_apple_sanitizer_dylibs(builder, &builder.native_dir(target), "osx", &libdir);
}
}
}

fn copy_apple_sanitizer_dylibs(
/// Copies sanitizer runtime libraries into target libdir.
fn copy_sanitizers(
builder: &Builder<'_>,
native_dir: &Path,
platform: &str,
into: &Path,
) {
for &sanitizer in &["asan", "tsan"] {
let filename = format!("lib__rustc__clang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
let mut src_path = native_dir.join(sanitizer);
src_path.push("build");
src_path.push("lib");
src_path.push("darwin");
src_path.push(&filename);
builder.copy(&src_path, &into.join(filename));
compiler: &Compiler,
target: Interned<String>,
) -> Vec<PathBuf> {
let runtimes: Vec<native::SanitizerRuntime> = builder.ensure(native::Sanitizers { target });

if builder.config.dry_run {
return Vec::new();
}

let mut target_deps = Vec::new();
let libdir = builder.sysroot_libdir(*compiler, target);

for runtime in &runtimes {
let dst = libdir.join(&runtime.name);
builder.copy(&runtime.path, &dst);

if target == "x86_64-apple-darwin" {
// Update the library install name reflect the fact it has been renamed.
let status = Command::new("install_name_tool")
.arg("-id")
.arg(format!("@rpath/{}", runtime.name))
.arg(&dst)
.status()
.expect("failed to execute `install_name_tool`");
assert!(status.success());
}

target_deps.push(dst);
}

target_deps
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
4 changes: 0 additions & 4 deletions src/bootstrap/dist.rs
Original file line number Diff line number Diff line change
@@ -984,10 +984,6 @@ impl Step for Src {
"src/libcore",
"src/libpanic_abort",
"src/libpanic_unwind",
"src/librustc_asan",
"src/librustc_lsan",
"src/librustc_msan",
"src/librustc_tsan",
"src/libstd",
"src/libunwind",
"src/libtest",
2 changes: 1 addition & 1 deletion src/bootstrap/doc.rs
Original file line number Diff line number Diff line change
@@ -449,7 +449,7 @@ impl Step for Std {

let run_cargo_rustdoc_for = |package: &str| {
let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc");
compile::std_cargo(builder, &compiler, target, &mut cargo);
compile::std_cargo(builder, target, &mut cargo);

// Keep a whitelist so we do not build internal stdlib crates, these will be
// build by the rustc step later if enabled.
115 changes: 115 additions & 0 deletions src/bootstrap/native.rs
Original file line number Diff line number Diff line change
@@ -546,3 +546,118 @@ impl Step for TestHelpers {
.compile("rust_test_helpers");
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Sanitizers {
pub target: Interned<String>,
}

impl Step for Sanitizers {
type Output = Vec<SanitizerRuntime>;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/llvm-project/compiler-rt").path("src/sanitizers")
}

fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Sanitizers { target: run.target });
}

/// Builds sanitizer runtime libraries.
fn run(self, builder: &Builder<'_>) -> Self::Output {
let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
if !compiler_rt_dir.exists() {
return Vec::new();
}

let out_dir = builder.native_dir(self.target).join("sanitizers");
let runtimes = supported_sanitizers(&out_dir, self.target);
if runtimes.is_empty() {
return runtimes;
}

let llvm_config = builder.ensure(Llvm { target: builder.config.build });
if builder.config.dry_run {
return runtimes;
}

let done_stamp = out_dir.join("sanitizers-finished-building");
if done_stamp.exists() {
builder.info(&format!(
"Assuming that sanitizers rebuild is not necessary. \
To force a rebuild, remove the file `{}`",
done_stamp.display()
));
return runtimes;
}

builder.info(&format!("Building sanitizers for {}", self.target));
let _time = util::timeit(&builder);

let mut cfg = cmake::Config::new(&compiler_rt_dir);
cfg.target(&self.target);
cfg.host(&builder.config.build);
cfg.profile("Release");

cfg.define("CMAKE_C_COMPILER_TARGET", self.target);
cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
cfg.define("LLVM_CONFIG_PATH", &llvm_config);

t!(fs::create_dir_all(&out_dir));
cfg.out_dir(out_dir);

for runtime in &runtimes {
cfg.build_target(&runtime.cmake_target);
cfg.build();
}

t!(fs::write(&done_stamp, b""));

runtimes
}
}

#[derive(Clone, Debug)]
pub struct SanitizerRuntime {
/// CMake target used to build the runtime.
pub cmake_target: String,
/// Path to the built runtime library.
pub path: PathBuf,
/// Library filename that will be used rustc.
pub name: String,
}

/// Returns sanitizers available on a given target.
fn supported_sanitizers(out_dir: &Path, target: Interned<String>) -> Vec<SanitizerRuntime> {
let mut result = Vec::new();
match &*target {
"x86_64-apple-darwin" => {
for s in &["asan", "lsan", "tsan"] {
result.push(SanitizerRuntime {
cmake_target: format!("clang_rt.{}_osx_dynamic", s),
path: out_dir
.join(&format!("build/lib/darwin/libclang_rt.{}_osx_dynamic.dylib", s)),
name: format!("librustc_rt.{}.dylib", s),
});
}
}
"x86_64-unknown-linux-gnu" => {
for s in &["asan", "lsan", "msan", "tsan"] {
result.push(SanitizerRuntime {
cmake_target: format!("clang_rt.{}-x86_64", s),
path: out_dir.join(&format!("build/lib/linux/libclang_rt.{}-x86_64.a", s)),
name: format!("librustc_rt.{}.a", s),
});
}
}
_ => {}
}
result
}
2 changes: 1 addition & 1 deletion src/bootstrap/test.rs
Original file line number Diff line number Diff line change
@@ -1659,7 +1659,7 @@ impl Step for Crate {
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
match mode {
Mode::Std => {
compile::std_cargo(builder, &compiler, target, &mut cargo);
compile::std_cargo(builder, target, &mut cargo);
}
Mode::Rustc => {
builder.ensure(compile::Rustc { compiler, target });
104 changes: 0 additions & 104 deletions src/build_helper/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::fs::File;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thread;
use std::time::{SystemTime, UNIX_EPOCH};
use std::{env, fs};

@@ -181,108 +179,6 @@ pub fn up_to_date(src: &Path, dst: &Path) -> bool {
}
}

#[must_use]
pub struct NativeLibBoilerplate {
pub src_dir: PathBuf,
pub out_dir: PathBuf,
}

impl NativeLibBoilerplate {
/// On macOS we don't want to ship the exact filename that compiler-rt builds.
/// This conflicts with the system and ours is likely a wildly different
/// version, so they can't be substituted.
///
/// As a result, we rename it here but we need to also use
/// `install_name_tool` on macOS to rename the commands listed inside of it to
/// ensure it's linked against correctly.
pub fn fixup_sanitizer_lib_name(&self, sanitizer_name: &str) {
if env::var("TARGET").unwrap() != "x86_64-apple-darwin" {
return;
}

let dir = self.out_dir.join("build/lib/darwin");
let name = format!("clang_rt.{}_osx_dynamic", sanitizer_name);
let src = dir.join(&format!("lib{}.dylib", name));
let new_name = format!("lib__rustc__{}.dylib", name);
let dst = dir.join(&new_name);

println!("{} => {}", src.display(), dst.display());
fs::rename(&src, &dst).unwrap();
let status = Command::new("install_name_tool")
.arg("-id")
.arg(format!("@rpath/{}", new_name))
.arg(&dst)
.status()
.expect("failed to execute `install_name_tool`");
assert!(status.success());
}
}

impl Drop for NativeLibBoilerplate {
fn drop(&mut self) {
if !thread::panicking() {
t!(File::create(self.out_dir.join("rustbuild.timestamp")));
}
}
}

// Perform standard preparations for native libraries that are build only once for all stages.
// Emit rerun-if-changed and linking attributes for Cargo, check if any source files are
// updated, calculate paths used later in actual build with CMake/make or C/C++ compiler.
// If Err is returned, then everything is up-to-date and further build actions can be skipped.
// Timestamps are created automatically when the result of `native_lib_boilerplate` goes out
// of scope, so all the build actions should be completed until then.
pub fn native_lib_boilerplate(
src_dir: &Path,
out_name: &str,
link_name: &str,
search_subdir: &str,
) -> Result<NativeLibBoilerplate, ()> {
rerun_if_changed_anything_in_dir(src_dir);

let out_dir =
env::var_os("RUSTBUILD_NATIVE_DIR").unwrap_or_else(|| env::var_os("OUT_DIR").unwrap());
let out_dir = PathBuf::from(out_dir).join(out_name);
t!(fs::create_dir_all(&out_dir));
if link_name.contains('=') {
println!("cargo:rustc-link-lib={}", link_name);
} else {
println!("cargo:rustc-link-lib=static={}", link_name);
}
println!("cargo:rustc-link-search=native={}", out_dir.join(search_subdir).display());

let timestamp = out_dir.join("rustbuild.timestamp");
if !up_to_date(Path::new("build.rs"), &timestamp) || !up_to_date(src_dir, &timestamp) {
Ok(NativeLibBoilerplate { src_dir: src_dir.to_path_buf(), out_dir })
} else {
Err(())
}
}

pub fn sanitizer_lib_boilerplate(
sanitizer_name: &str,
) -> Result<(NativeLibBoilerplate, String), ()> {
let (link_name, search_path, apple) = match &*env::var("TARGET").unwrap() {
"x86_64-unknown-linux-gnu" => {
(format!("clang_rt.{}-x86_64", sanitizer_name), "build/lib/linux", false)
}
"x86_64-apple-darwin" => {
(format!("clang_rt.{}_osx_dynamic", sanitizer_name), "build/lib/darwin", true)
}
_ => return Err(()),
};
let to_link = if apple {
format!("dylib=__rustc__{}", link_name)
} else {
format!("static={}", link_name)
};
// This env var is provided by rustbuild to tell us where `compiler-rt`
// lives.
let dir = env::var_os("RUST_COMPILER_RT_ROOT").unwrap();
let lib = native_lib_boilerplate(dir.as_ref(), sanitizer_name, &to_link, search_path)?;
Ok((lib, link_name))
}

fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
let meta = t!(e.metadata());
163 changes: 163 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# `sanitizer`

The tracking issue for this feature is: [#39699](https://github.com/rust-lang/rust/issues/39699).

------------------------

This feature allows for use of one of following sanitizers:

* [AddressSanitizer][clang-asan] a faster memory error detector. Can
detect out-of-bounds access to heap, stack, and globals, use after free, use
after return, double free, invalid free, memory leaks.
* [LeakSanitizer][clang-lsan] a run-time memory leak detector.
* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
* [ThreadSanitizer][clang-tsan] a fast data race detector.

To enable a sanitizer compile with `-Zsanitizer=...` option, where value is one
of `address`, `leak`, `memory` or `thread`.

# Examples

This sections show various issues that can be detected with sanitizers. For
simplicity, the examples are prepared under assumption that optimization level
used is zero.

## AddressSanitizer

Stack buffer overflow:

```shell
$ cat a.rs
fn main() {
let xs = [0, 1, 2, 3];
let _y = unsafe { *xs.as_ptr().offset(4) };
}
$ rustc -Zsanitizer=address a.rs
$ ./a
=================================================================
==10029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcc15f43d0 at pc 0x55f77dc015c5 bp 0x7ffcc15f4390 sp 0x7ffcc15f4388
READ of size 4 at 0x7ffcc15f43d0 thread T0
#0 0x55f77dc015c4 in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa5c4)
#1 0x55f77dc01cdb in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::haa8c76d1faa7b7ca (/tmp/a+0xacdb)
#2 0x55f77dc90f02 in std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::hfeb9a1aef9ac820d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:48:12
#3 0x55f77dc90f02 in std::panicking::try::do_call::h12f0919717b8e0a6 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:288:39
#4 0x55f77dc926c9 in __rust_maybe_catch_panic /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libpanic_unwind/lib.rs:80:7
#5 0x55f77dc9197c in std::panicking::try::h413b21cdcd6cfd86 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:267:12
#6 0x55f77dc9197c in std::panic::catch_unwind::hc5cc8ef2fd73424d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panic.rs:396:8
#7 0x55f77dc9197c in std::rt::lang_start_internal::h2039f418ab92218f /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:47:24
#8 0x55f77dc01c61 in std::rt::lang_start::ha905d28f6b61d691 (/tmp/a+0xac61)
#9 0x55f77dc0163a in main (/tmp/a+0xa63a)
#10 0x7f9b3cf5bbba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba)
#11 0x55f77dc01289 in _start (/tmp/a+0xa289)

Address 0x7ffcc15f43d0 is located in stack of thread T0 at offset 48 in frame
#0 0x55f77dc0135f in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa35f)

This frame has 1 object(s):
[32, 48) 'xs' <== Memory access at offset 48 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/a+0xa5c4) in a::main::hab3bd2a745c2d0ac
Shadow bytes around the buggy address:
0x1000182b6820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b6830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b6840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b6850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b6860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000182b6870: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00
0x1000182b6880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b6890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b68a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b68b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000182b68c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==10029==ABORTING
```
## MemorySanitizer
Use of uninitialized memory. Note that we are using `-Zbuild-std` to instrument
standard library, and passing `-msan-track-origins=2` to the LLVM to track
origins of uninitialized memory:
```shell
$ cat src/main.rs
use std::mem::MaybeUninit;

fn main() {
unsafe {
let a = MaybeUninit::<[usize; 4]>::uninit();
let a = a.assume_init();
println!("{}", a[2]);
}
}

$ env RUSTFLAGS="-Zsanitizer=memory -Cllvm-args=-msan-track-origins=2" cargo -Zbuild-std run --target x86_64-unknown-linux-gnu
==9416==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x560c04f7488a in core::fmt::num::imp::fmt_u64::haa293b0b098501ca $RUST/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/src/libcore/fmt/num.rs:202:16
...
Uninitialized value was stored to memory at
#0 0x560c04ae898a in __msan_memcpy.part.0 $RUST/src/llvm-project/compiler-rt/lib/msan/msan_interceptors.cc:1558:3
#1 0x560c04b2bf88 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:6:16

Uninitialized value was created by an allocation of 'a' in the stack frame of function '_ZN6memory4main17hd2333c1899d997f5E'
#0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3
```
# Instrumentation of external dependencies and std
The sanitizers to varying degrees work correctly with partially instrumented
code. On the one extreme is LeakSanitizer that doesn't use any compile time
instrumentation, on the other is MemorySanitizer that requires that all program
code to be instrumented (failing to achieve that will inevitably result in
false positives).
It is strongly recommended to combine sanitizers with recompiled and
instrumented standard library, for example using [cargo `-Zbuild-std`
functionality][build-std].
[build-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
# Build scripts and procedural macros
Use of sanitizers together with build scripts and procedural macros is
technically possible, but in almost all cases it would be best avoided. This
is especially true for procedural macros which would require an instrumented
version of rustc.
In more practical terms when using cargo always remember to pass `--target`
flag, so that rustflags will not be applied to build scripts and procedural
macros.
# Additional Information
* [Sanitizers project page](https://github.com/google/sanitizers/wiki/)
* [AddressSanitizer in Clang][clang-asan]
* [LeakSanitizer in Clang][clang-lsan]
* [MemorySanitizer in Clang][clang-msan]
* [ThreadSanitizer in Clang][clang-tsan]
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html

This file was deleted.

4 changes: 0 additions & 4 deletions src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
@@ -686,10 +686,6 @@ rustc_queries! {
fatal_cycle
desc { "checking if the crate has_panic_handler" }
}
query is_sanitizer_runtime(_: CrateNum) -> bool {
fatal_cycle
desc { "query a crate is `#![sanitizer_runtime]`" }
}
query is_profiler_runtime(_: CrateNum) -> bool {
fatal_cycle
desc { "query a crate is `#![profiler_runtime]`" }
20 changes: 0 additions & 20 deletions src/librustc_asan/Cargo.toml

This file was deleted.

30 changes: 0 additions & 30 deletions src/librustc_asan/build.rs

This file was deleted.

10 changes: 0 additions & 10 deletions src/librustc_asan/lib.rs

This file was deleted.

89 changes: 42 additions & 47 deletions src/librustc_codegen_ssa/back/link.rs
Original file line number Diff line number Diff line change
@@ -531,6 +531,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(

{
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu);
link_sanitizer_runtime(sess, crate_type, &mut *linker);
link_args::<B>(
&mut *linker,
flavor,
@@ -735,6 +736,47 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
}
}

fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: &mut dyn Linker) {
let sanitizer = match &sess.opts.debugging_opts.sanitizer {
Some(s) => s,
None => return,
};

if crate_type != config::CrateType::Executable {
return;
}

let name = match sanitizer {
Sanitizer::Address => "asan",
Sanitizer::Leak => "lsan",
Sanitizer::Memory => "msan",
Sanitizer::Thread => "tsan",
};

let default_sysroot = filesearch::get_or_default_sysroot();
let default_tlib =
filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple());

match sess.opts.target_triple.triple() {
"x86_64-apple-darwin" => {
// On Apple platforms, the sanitizer is always built as a dylib, and
// LLVM will link to `@rpath/*.dylib`, so we need to specify an
// rpath to the library as well (the rpath should be absolute, see
// PR #41352 for details).
let libname = format!("rustc_rt.{}", name);
let rpath = default_tlib.to_str().expect("non-utf8 component in path");
linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
linker.link_dylib(Symbol::intern(&libname));
}
"x86_64-unknown-linux-gnu" => {
let filename = format!("librustc_rt.{}.a", name);
let path = default_tlib.join(&filename);
linker.link_whole_rlib(&path);
}
_ => {}
}
}

/// Returns a boolean indicating whether the specified crate should be ignored
/// during LTO.
///
@@ -1415,12 +1457,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
}
_ if codegen_results.crate_info.sanitizer_runtime == Some(cnum)
&& crate_type == config::CrateType::Executable =>
{
// Link the sanitizer runtimes only if we are actually producing an executable
link_sanitizer_runtime::<B>(cmd, sess, codegen_results, tmpdir, cnum);
}
// compiler-builtins are always placed last to ensure that they're
// linked correctly.
_ if codegen_results.crate_info.compiler_builtins == Some(cnum) => {
@@ -1457,47 +1493,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
}
}

// We must link the sanitizer runtime using -Wl,--whole-archive but since
// it's packed in a .rlib, it contains stuff that are not objects that will
// make the linker error. So we must remove those bits from the .rlib before
// linking it.
fn link_sanitizer_runtime<'a, B: ArchiveBuilder<'a>>(
cmd: &mut dyn Linker,
sess: &'a Session,
codegen_results: &CodegenResults,
tmpdir: &Path,
cnum: CrateNum,
) {
let src = &codegen_results.crate_info.used_crate_source[&cnum];
let cratepath = &src.rlib.as_ref().unwrap().0;

if sess.target.target.options.is_like_osx {
// On Apple platforms, the sanitizer is always built as a dylib, and
// LLVM will link to `@rpath/*.dylib`, so we need to specify an
// rpath to the library as well (the rpath should be absolute, see
// PR #41352 for details).
//
// FIXME: Remove this logic into librustc_*san once Cargo supports it
let rpath = cratepath.parent().unwrap();
let rpath = rpath.to_str().expect("non-utf8 component in path");
cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
}

let dst = tmpdir.join(cratepath.file_name().unwrap());
let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
archive.update_symbols();

for f in archive.src_files() {
if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
archive.remove_file(&f);
}
}

archive.build();

cmd.link_whole_rlib(&dst);
}

// Adds the static "rlib" versions of all crates to the command line.
// There's a bit of magic which happens here specifically related to LTO and
// dynamic libraries. Specifically:
4 changes: 0 additions & 4 deletions src/librustc_codegen_ssa/base.rs
Original file line number Diff line number Diff line change
@@ -723,7 +723,6 @@ impl CrateInfo {
panic_runtime: None,
compiler_builtins: None,
profiler_runtime: None,
sanitizer_runtime: None,
is_no_builtins: Default::default(),
native_libraries: Default::default(),
used_libraries: tcx.native_libraries(LOCAL_CRATE),
@@ -759,9 +758,6 @@ impl CrateInfo {
if tcx.is_profiler_runtime(cnum) {
info.profiler_runtime = Some(cnum);
}
if tcx.is_sanitizer_runtime(cnum) {
info.sanitizer_runtime = Some(cnum);
}
if tcx.is_no_builtins(cnum) {
info.is_no_builtins.insert(cnum);
}
1 change: 0 additions & 1 deletion src/librustc_codegen_ssa/lib.rs
Original file line number Diff line number Diff line change
@@ -122,7 +122,6 @@ pub struct CrateInfo {
pub panic_runtime: Option<CrateNum>,
pub compiler_builtins: Option<CrateNum>,
pub profiler_runtime: Option<CrateNum>,
pub sanitizer_runtime: Option<CrateNum>,
pub is_no_builtins: FxHashSet<CrateNum>,
pub native_libraries: FxHashMap<CrateNum, Lrc<Vec<NativeLibrary>>>,
pub crate_name: FxHashMap<CrateNum, String>,
3 changes: 0 additions & 3 deletions src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
@@ -192,9 +192,6 @@ declare_features! (
/// Allows using the `unadjusted` ABI; perma-unstable.
(active, abi_unadjusted, "1.16.0", None, None),

/// Allows identifying crates that contain sanitizer runtimes.
(active, sanitizer_runtime, "1.17.0", None, None),

/// Used to identify crates that contain the profiler runtime.
(active, profiler_runtime, "1.18.0", None, None),

5 changes: 0 additions & 5 deletions src/librustc_feature/builtin_attrs.rs
Original file line number Diff line number Diff line change
@@ -409,11 +409,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
which contains compiler-rt intrinsics and will never be stable",
),
gated!(
sanitizer_runtime, Whitelisted, template!(Word),
"the `#[sanitizer_runtime]` attribute is used to identify crates that contain the runtime \
of a sanitizer and will never be stable",
),
gated!(
profiler_runtime, Whitelisted, template!(Word),
"the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
2 changes: 2 additions & 0 deletions src/librustc_feature/removed.rs
Original file line number Diff line number Diff line change
@@ -74,6 +74,8 @@ declare_features! (
(removed, pushpop_unsafe, "1.2.0", None, None, None),
(removed, needs_allocator, "1.4.0", Some(27389), None,
Some("subsumed by `#![feature(allocator_internals)]`")),
/// Allows identifying crates that contain sanitizer runtimes.
(removed, sanitizer_runtime, "1.17.0", None, None, None),
(removed, proc_macro_mod, "1.27.0", Some(54727), None,
Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
(removed, proc_macro_expr, "1.27.0", Some(54727), None,
20 changes: 0 additions & 20 deletions src/librustc_lsan/Cargo.toml

This file was deleted.

29 changes: 0 additions & 29 deletions src/librustc_lsan/build.rs

This file was deleted.

10 changes: 0 additions & 10 deletions src/librustc_lsan/lib.rs

This file was deleted.

105 changes: 1 addition & 104 deletions src/librustc_metadata/creader.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob
use rustc::hir::map::Definitions;
use rustc::middle::cstore::DepKind;
use rustc::middle::cstore::{CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn};
use rustc::session::config::{self, Sanitizer};
use rustc::session::config;
use rustc::session::search_paths::PathKind;
use rustc::session::{CrateDisambiguator, Session};
use rustc::ty::TyCtxt;
@@ -674,108 +674,6 @@ impl<'a> CrateLoader<'a> {
self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime());
}

fn inject_sanitizer_runtime(&mut self) {
if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
// Sanitizers can only be used on some tested platforms with
// executables linked to `std`
const ASAN_SUPPORTED_TARGETS: &[&str] =
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
const TSAN_SUPPORTED_TARGETS: &[&str] =
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];

let supported_targets = match *sanitizer {
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
};
if !supported_targets.contains(&&*self.sess.opts.target_triple.triple()) {
self.sess.err(&format!(
"{:?}Sanitizer only works with the `{}` target",
sanitizer,
supported_targets.join("` or `")
));
return;
}

// firstyear 2017 - during testing I was unable to access an OSX machine
// to make this work on different crate types. As a result, today I have
// only been able to test and support linux as a target.
if self.sess.opts.target_triple.triple() == "x86_64-unknown-linux-gnu" {
if !self.sess.crate_types.borrow().iter().all(|ct| {
match *ct {
// Link the runtime
config::CrateType::Executable => true,
// This crate will be compiled with the required
// instrumentation pass
config::CrateType::Staticlib
| config::CrateType::Rlib
| config::CrateType::Dylib
| config::CrateType::Cdylib => false,
_ => {
self.sess.err(&format!(
"Only executables, staticlibs, \
cdylibs, dylibs and rlibs can be compiled with \
`-Z sanitizer`"
));
false
}
}
}) {
return;
}
} else {
if !self.sess.crate_types.borrow().iter().all(|ct| {
match *ct {
// Link the runtime
config::CrateType::Executable => true,
// This crate will be compiled with the required
// instrumentation pass
config::CrateType::Rlib => false,
_ => {
self.sess.err(&format!(
"Only executables and rlibs can be \
compiled with `-Z sanitizer`"
));
false
}
}
}) {
return;
}
}

let mut uses_std = false;
self.cstore.iter_crate_data(|_, data| {
if data.name() == sym::std {
uses_std = true;
}
});

if uses_std {
let name = Symbol::intern(match sanitizer {
Sanitizer::Address => "rustc_asan",
Sanitizer::Leak => "rustc_lsan",
Sanitizer::Memory => "rustc_msan",
Sanitizer::Thread => "rustc_tsan",
});
info!("loading sanitizer: {}", name);

let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Explicit, None);
let data = self.cstore.get_crate_data(cnum);

// Sanity check the loaded crate to ensure it is indeed a sanitizer runtime
if !data.is_sanitizer_runtime() {
self.sess.err(&format!("the crate `{}` is not a sanitizer runtime", name));
}
} else {
self.sess.err("Must link std to be compiled with `-Z sanitizer`");
}
}
}

fn inject_profiler_runtime(&mut self) {
if self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled() {
info!("loading profiler");
@@ -927,7 +825,6 @@ impl<'a> CrateLoader<'a> {
}

pub fn postprocess(&mut self, krate: &ast::Crate) {
self.inject_sanitizer_runtime();
self.inject_profiler_runtime();
self.inject_allocator_crate(krate);
self.inject_panic_runtime(krate);
4 changes: 0 additions & 4 deletions src/librustc_metadata/rmeta/decoder.rs
Original file line number Diff line number Diff line change
@@ -1587,10 +1587,6 @@ impl<'a, 'tcx> CrateMetadata {
self.root.panic_runtime
}

crate fn is_sanitizer_runtime(&self) -> bool {
self.root.sanitizer_runtime
}

crate fn is_profiler_runtime(&self) -> bool {
self.root.profiler_runtime
}
1 change: 0 additions & 1 deletion src/librustc_metadata/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
@@ -161,7 +161,6 @@ provide! { <'tcx> tcx, def_id, other, cdata,
is_compiler_builtins => { cdata.root.compiler_builtins }
has_global_allocator => { cdata.root.has_global_allocator }
has_panic_handler => { cdata.root.has_panic_handler }
is_sanitizer_runtime => { cdata.root.sanitizer_runtime }
is_profiler_runtime => { cdata.root.profiler_runtime }
panic_strategy => { cdata.root.panic_strategy }
extern_crate => {
1 change: 0 additions & 1 deletion src/librustc_metadata/rmeta/encoder.rs
Original file line number Diff line number Diff line change
@@ -514,7 +514,6 @@ impl<'tcx> EncodeContext<'tcx> {
no_builtins: attr::contains_name(&attrs, sym::no_builtins),
panic_runtime: attr::contains_name(&attrs, sym::panic_runtime),
profiler_runtime: attr::contains_name(&attrs, sym::profiler_runtime),
sanitizer_runtime: attr::contains_name(&attrs, sym::sanitizer_runtime),
symbol_mangling_version: tcx.sess.opts.debugging_opts.symbol_mangling_version,

crate_deps,
1 change: 0 additions & 1 deletion src/librustc_metadata/rmeta/mod.rs
Original file line number Diff line number Diff line change
@@ -209,7 +209,6 @@ crate struct CrateRoot<'tcx> {
no_builtins: bool,
panic_runtime: bool,
profiler_runtime: bool,
sanitizer_runtime: bool,
symbol_mangling_version: SymbolManglingVersion,
}

20 changes: 0 additions & 20 deletions src/librustc_msan/Cargo.toml

This file was deleted.

29 changes: 0 additions & 29 deletions src/librustc_msan/build.rs

This file was deleted.

10 changes: 0 additions & 10 deletions src/librustc_msan/lib.rs

This file was deleted.

26 changes: 26 additions & 0 deletions src/librustc_session/session.rs
Original file line number Diff line number Diff line change
@@ -1124,6 +1124,32 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
See https://github.com/rust-lang/rust/issues/61002 for details.",
);
}

// Sanitizers can only be used on some tested platforms.
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
const ASAN_SUPPORTED_TARGETS: &[&str] =
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
const TSAN_SUPPORTED_TARGETS: &[&str] =
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
const LSAN_SUPPORTED_TARGETS: &[&str] =
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];

let supported_targets = match *sanitizer {
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
};

if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
sess.err(&format!(
"{:?}Sanitizer only works with the `{}` target",
sanitizer,
supported_targets.join("` or `")
));
}
}
}

/// Hash value constructed out of all the `-C metadata` arguments passed to the
20 changes: 0 additions & 20 deletions src/librustc_tsan/Cargo.toml

This file was deleted.

30 changes: 0 additions & 30 deletions src/librustc_tsan/build.rs

This file was deleted.

10 changes: 0 additions & 10 deletions src/librustc_tsan/lib.rs

This file was deleted.

10 changes: 0 additions & 10 deletions src/libstd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -34,16 +34,6 @@ features = [ "rustc-dep-of-std" ] # enable build support for integrating into li
[dev-dependencies]
rand = "0.7"

[target.x86_64-apple-darwin.dependencies]
rustc_asan = { path = "../librustc_asan" }
rustc_tsan = { path = "../librustc_tsan" }

[target.x86_64-unknown-linux-gnu.dependencies]
rustc_asan = { path = "../librustc_asan" }
rustc_lsan = { path = "../librustc_lsan" }
rustc_msan = { path = "../librustc_msan" }
rustc_tsan = { path = "../librustc_tsan" }

[target.'cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] }

2 changes: 1 addition & 1 deletion src/test/run-make-fulldeps/sanitizer-address/Makefile
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ endif
endif

all:
$(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | $(CGREP) librustc_asan
$(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | $(CGREP) rustc_rt.asan
# Verify that stack buffer overflow is detected:
$(TMPDIR)/overflow 2>&1 | $(CGREP) stack-buffer-overflow
# Verify that variable name is included in address sanitizer report:
16 changes: 0 additions & 16 deletions src/test/run-make-fulldeps/sanitizer-invalid-cratetype/Makefile

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -2,4 +2,4 @@

all:
$(RUSTC) -Z sanitizer=leak --target i686-unknown-linux-gnu hello.rs 2>&1 | \
$(CGREP) 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` target'
$(CGREP) 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target'
2 changes: 1 addition & 1 deletion src/test/run-make-fulldeps/sanitizer-leak/Makefile
Original file line number Diff line number Diff line change
@@ -7,5 +7,5 @@
# FIXME(#46126) ThinLTO for libstd broke this test

all:
$(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | $(CGREP) librustc_lsan
$(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | $(CGREP) rustc_rt.lsan
$(TMPDIR)/leak 2>&1 | $(CGREP) 'detected memory leaks'
4 changes: 2 additions & 2 deletions src/test/run-make-fulldeps/sanitizer-memory/Makefile
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
# only-x86_64

all:
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) librustc_msan
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) rustc_rt.msan
$(TMPDIR)/uninit 2>&1 | $(CGREP) use-of-uninitialized-value
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args maybeuninit.rs | $(CGREP) librustc_msan
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args maybeuninit.rs | $(CGREP) rustc_rt.msan
$(TMPDIR)/maybeuninit 2>&1 | $(CGREP) use-of-uninitialized-value
3 changes: 0 additions & 3 deletions src/test/ui/feature-gates/feature-gate-sanitizer-runtime.rs

This file was deleted.

11 changes: 0 additions & 11 deletions src/test/ui/feature-gates/feature-gate-sanitizer-runtime.stderr

This file was deleted.