Auto merge of #65241 - tmiasko:no-std-san, r=alexcrichton
build-std compatible sanitizer support ### Motivation When using `-Z sanitizer=*` feature it is essential that both user code and standard library is instrumented. Otherwise the utility of sanitizer will be limited, or its use will be impractical like in the case of memory sanitizer. The recently introduced cargo feature build-std makes it possible to rebuild standard library with arbitrary rustc flags. Unfortunately, those changes alone do not make it easy to rebuild standard library with sanitizers, since runtimes are dependencies of std that have to be build in specific environment, generally not available outside rustbuild process. Additionally rebuilding them requires presence of llvm-config and compiler-rt sources. The goal of changes proposed here is to make it possible to avoid rebuilding sanitizer runtimes when rebuilding the std, thus making it possible to instrument standard library for use with sanitizer with simple, although verbose command: ``` env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=-Zsanitizer=thread cargo test -Zbuild-std --target x86_64-unknown-linux-gnu ``` ### Implementation * Sanitizer runtimes are no long packed into crates. Instead, libraries build from compiler-rt are used as is, after renaming them into `librusc_rt.*`. * rustc obtains runtimes from target libdir for default sysroot, so that they are not required in custom build sysroots created with build-std. * The runtimes are only linked-in into executables to address issue #64629. (in previous design it was hard to avoid linking runtimes into static libraries produced by rustc as demonstrated by sanitizer-staticlib-link test, which still passes despite changes made in #64780). cc @kennytm, @japaric, @firstyear, @choller
This commit is contained in:
commit
e621797264
48
Cargo.lock
48
Cargo.lock
@ -3345,17 +3345,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"
|
||||
@ -3674,17 +3663,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"
|
||||
@ -3746,17 +3724,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"
|
||||
@ -3929,17 +3896,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"
|
||||
@ -4301,10 +4257,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",
|
||||
]
|
||||
|
@ -343,6 +343,7 @@ impl<'a> Builder<'a> {
|
||||
tool::Rustdoc,
|
||||
tool::Clippy,
|
||||
native::Llvm,
|
||||
native::Sanitizers,
|
||||
tool::Rustfmt,
|
||||
tool::Miri,
|
||||
native::Lld
|
||||
|
@ -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(
|
||||
|
@ -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)]
|
||||
|
@ -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",
|
||||
|
@ -391,7 +391,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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 });
|
||||
|
@ -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"), ×tamp) || !up_to_date(src_dir, ×tamp) {
|
||||
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
src/doc/unstable-book/src/compiler-flags/sanitizer.md
Normal file
163
src/doc/unstable-book/src/compiler-flags/sanitizer.md
Normal file
@ -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
|
@ -1,5 +0,0 @@
|
||||
# `sanitizer_runtime_lib`
|
||||
|
||||
This feature is internal to the Rust compiler and is not intended for general use.
|
||||
|
||||
------------------------
|
@ -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]`" }
|
||||
|
@ -1,20 +0,0 @@
|
||||
[package]
|
||||
authors = ["The Rust Project Developers"]
|
||||
build = "build.rs"
|
||||
name = "rustc_asan"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "rustc_asan"
|
||||
path = "lib.rs"
|
||||
test = false
|
||||
|
||||
[build-dependencies]
|
||||
build_helper = { path = "../build_helper" }
|
||||
cmake = "0.1.38"
|
||||
|
||||
[dependencies]
|
||||
alloc = { path = "../liballoc" }
|
||||
core = { path = "../libcore" }
|
||||
compiler_builtins = "0.1.0"
|
@ -1,30 +0,0 @@
|
||||
use build_helper::sanitizer_lib_boilerplate;
|
||||
use std::env;
|
||||
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||
build_helper::restore_library_path();
|
||||
|
||||
let (native, target) = match sanitizer_lib_boilerplate("asan") {
|
||||
Ok(native) => native,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
Config::new(&native.src_dir)
|
||||
.define("COMPILER_RT_BUILD_SANITIZERS", "ON")
|
||||
.define("COMPILER_RT_BUILD_BUILTINS", "OFF")
|
||||
.define("COMPILER_RT_BUILD_XRAY", "OFF")
|
||||
.define("LLVM_CONFIG_PATH", llvm_config)
|
||||
.out_dir(&native.out_dir)
|
||||
.build_target(&target)
|
||||
.build();
|
||||
native.fixup_sanitizer_lib_name("asan");
|
||||
}
|
||||
println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#![sanitizer_runtime]
|
||||
#![feature(nll)]
|
||||
#![feature(sanitizer_runtime)]
|
||||
#![feature(staged_api)]
|
||||
#![no_std]
|
||||
#![unstable(
|
||||
feature = "sanitizer_runtime_lib",
|
||||
reason = "internal implementation detail of sanitizers",
|
||||
issue = "none"
|
||||
)]
|
@ -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:
|
||||
|
@ -730,7 +730,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),
|
||||
@ -766,9 +765,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);
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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,
|
||||
|
@ -1,20 +0,0 @@
|
||||
[package]
|
||||
authors = ["The Rust Project Developers"]
|
||||
build = "build.rs"
|
||||
name = "rustc_lsan"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "rustc_lsan"
|
||||
path = "lib.rs"
|
||||
test = false
|
||||
|
||||
[build-dependencies]
|
||||
build_helper = { path = "../build_helper" }
|
||||
cmake = "0.1.38"
|
||||
|
||||
[dependencies]
|
||||
alloc = { path = "../liballoc" }
|
||||
core = { path = "../libcore" }
|
||||
compiler_builtins = "0.1.0"
|
@ -1,29 +0,0 @@
|
||||
use build_helper::sanitizer_lib_boilerplate;
|
||||
use std::env;
|
||||
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||
build_helper::restore_library_path();
|
||||
|
||||
let (native, target) = match sanitizer_lib_boilerplate("lsan") {
|
||||
Ok(native) => native,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
Config::new(&native.src_dir)
|
||||
.define("COMPILER_RT_BUILD_SANITIZERS", "ON")
|
||||
.define("COMPILER_RT_BUILD_BUILTINS", "OFF")
|
||||
.define("COMPILER_RT_BUILD_XRAY", "OFF")
|
||||
.define("LLVM_CONFIG_PATH", llvm_config)
|
||||
.out_dir(&native.out_dir)
|
||||
.build_target(&target)
|
||||
.build();
|
||||
}
|
||||
println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#![sanitizer_runtime]
|
||||
#![feature(nll)]
|
||||
#![feature(sanitizer_runtime)]
|
||||
#![feature(staged_api)]
|
||||
#![no_std]
|
||||
#![unstable(
|
||||
feature = "sanitizer_runtime_lib",
|
||||
reason = "internal implementation detail of sanitizers",
|
||||
issue = "none"
|
||||
)]
|
@ -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;
|
||||
@ -671,108 +671,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");
|
||||
@ -924,7 +822,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);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 => {
|
||||
|
@ -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,
|
||||
|
@ -209,7 +209,6 @@ crate struct CrateRoot<'tcx> {
|
||||
no_builtins: bool,
|
||||
panic_runtime: bool,
|
||||
profiler_runtime: bool,
|
||||
sanitizer_runtime: bool,
|
||||
symbol_mangling_version: SymbolManglingVersion,
|
||||
}
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
[package]
|
||||
authors = ["The Rust Project Developers"]
|
||||
build = "build.rs"
|
||||
name = "rustc_msan"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "rustc_msan"
|
||||
path = "lib.rs"
|
||||
test = false
|
||||
|
||||
[build-dependencies]
|
||||
build_helper = { path = "../build_helper" }
|
||||
cmake = "0.1.38"
|
||||
|
||||
[dependencies]
|
||||
alloc = { path = "../liballoc" }
|
||||
core = { path = "../libcore" }
|
||||
compiler_builtins = "0.1.0"
|
@ -1,29 +0,0 @@
|
||||
use build_helper::sanitizer_lib_boilerplate;
|
||||
use std::env;
|
||||
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||
build_helper::restore_library_path();
|
||||
|
||||
let (native, target) = match sanitizer_lib_boilerplate("msan") {
|
||||
Ok(native) => native,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
Config::new(&native.src_dir)
|
||||
.define("COMPILER_RT_BUILD_SANITIZERS", "ON")
|
||||
.define("COMPILER_RT_BUILD_BUILTINS", "OFF")
|
||||
.define("COMPILER_RT_BUILD_XRAY", "OFF")
|
||||
.define("LLVM_CONFIG_PATH", llvm_config)
|
||||
.out_dir(&native.out_dir)
|
||||
.build_target(&target)
|
||||
.build();
|
||||
}
|
||||
println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#![sanitizer_runtime]
|
||||
#![feature(nll)]
|
||||
#![feature(sanitizer_runtime)]
|
||||
#![feature(staged_api)]
|
||||
#![no_std]
|
||||
#![unstable(
|
||||
feature = "sanitizer_runtime_lib",
|
||||
reason = "internal implementation detail of sanitizers",
|
||||
issue = "none"
|
||||
)]
|
@ -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
|
||||
|
@ -1,20 +0,0 @@
|
||||
[package]
|
||||
authors = ["The Rust Project Developers"]
|
||||
build = "build.rs"
|
||||
name = "rustc_tsan"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "rustc_tsan"
|
||||
path = "lib.rs"
|
||||
test = false
|
||||
|
||||
[build-dependencies]
|
||||
build_helper = { path = "../build_helper" }
|
||||
cmake = "0.1.38"
|
||||
|
||||
[dependencies]
|
||||
alloc = { path = "../liballoc" }
|
||||
core = { path = "../libcore" }
|
||||
compiler_builtins = "0.1.0"
|
@ -1,30 +0,0 @@
|
||||
use build_helper::sanitizer_lib_boilerplate;
|
||||
use std::env;
|
||||
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||
build_helper::restore_library_path();
|
||||
|
||||
let (native, target) = match sanitizer_lib_boilerplate("tsan") {
|
||||
Ok(native) => native,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
Config::new(&native.src_dir)
|
||||
.define("COMPILER_RT_BUILD_SANITIZERS", "ON")
|
||||
.define("COMPILER_RT_BUILD_BUILTINS", "OFF")
|
||||
.define("COMPILER_RT_BUILD_XRAY", "OFF")
|
||||
.define("LLVM_CONFIG_PATH", llvm_config)
|
||||
.out_dir(&native.out_dir)
|
||||
.build_target(&target)
|
||||
.build();
|
||||
native.fixup_sanitizer_lib_name("tsan");
|
||||
}
|
||||
println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#![sanitizer_runtime]
|
||||
#![feature(nll)]
|
||||
#![feature(sanitizer_runtime)]
|
||||
#![feature(staged_api)]
|
||||
#![no_std]
|
||||
#![unstable(
|
||||
feature = "sanitizer_runtime_lib",
|
||||
reason = "internal implementation detail of sanitizers",
|
||||
issue = "none"
|
||||
)]
|
@ -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'] }
|
||||
|
||||
|
@ -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:
|
||||
|
@ -1,16 +0,0 @@
|
||||
# needs-sanitizer-support
|
||||
|
||||
-include ../tools.mk
|
||||
|
||||
# NOTE the address sanitizer only supports x86_64 linux and macOS
|
||||
|
||||
ifeq ($(TARGET),x86_64-apple-darwin)
|
||||
EXTRA_RUSTFLAG=-C rpath
|
||||
else
|
||||
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
|
||||
EXTRA_RUSTFLAG=
|
||||
endif
|
||||
endif
|
||||
|
||||
all:
|
||||
$(RUSTC) -Z sanitizer=address --crate-type proc-macro --target $(TARGET) hello.rs 2>&1 | $(CGREP) '-Z sanitizer'
|
@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -1,3 +0,0 @@
|
||||
#![sanitizer_runtime] //~ ERROR the `#[sanitizer_runtime]` attribute is
|
||||
|
||||
fn main() {}
|
@ -1,11 +0,0 @@
|
||||
error[E0658]: the `#[sanitizer_runtime]` attribute is used to identify crates that contain the runtime of a sanitizer and will never be stable
|
||||
--> $DIR/feature-gate-sanitizer-runtime.rs:1:1
|
||||
|
|
||||
LL | #![sanitizer_runtime]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(sanitizer_runtime)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
Loading…
Reference in New Issue
Block a user