about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-11-26 01:54:53 +0000
committerbors <bors@rust-lang.org>2019-11-26 01:54:53 +0000
commit2626f3d3d5c3007745176aa0fe22781b9ec2bb06 (patch)
treee793260388f0fa9a15bc39e7b80d55252096ae77
parent483a83b6e648d9e6cb21af75dba289a9aef150b1 (diff)
parentbf121a33c4f9c3361e29545c6448e603952e6944 (diff)
downloadrust-2626f3d3d5c3007745176aa0fe22781b9ec2bb06.tar.gz
rust-2626f3d3d5c3007745176aa0fe22781b9ec2bb06.zip
Auto merge of #66522 - tmiasko:sanitize-flags, r=alexcrichton
Add support for sanitizer recover and tracking origins of uninitialized memory

* Add support for sanitizer recovery `-Zsanitizer-recover=...` (equivalent to `-fsanitize-recover` in clang).
* Add support for tracking origins of uninitialized memory in MemorySanitizer `-Zsanitizer-memory-track-origins` (equivalent to `-fsanitize-memory-track-origins` in clang).
-rw-r--r--src/librustc/session/config.rs70
-rw-r--r--src/librustc_codegen_llvm/back/write.rs36
-rw-r--r--src/librustc_codegen_llvm/llvm/ffi.rs5
-rw-r--r--src/librustc_codegen_ssa/back/write.rs29
-rw-r--r--src/rustllvm/PassWrapper.cpp45
-rw-r--r--src/test/codegen/sanitizer-memory-track-orgins.rs28
-rw-r--r--src/test/codegen/sanitizer-recover.rs34
7 files changed, 215 insertions, 32 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 2b1dfcaf68c..5d01025c5b5 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -40,7 +40,7 @@ pub struct Config {
     pub usize_ty: UintTy,
 }
 
-#[derive(Clone, Hash, Debug)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
 pub enum Sanitizer {
     Address,
     Leak,
@@ -48,6 +48,19 @@ pub enum Sanitizer {
     Thread,
 }
 
+impl FromStr for Sanitizer {
+    type Err = ();
+    fn from_str(s: &str) -> Result<Sanitizer, ()> {
+        match s {
+            "address" => Ok(Sanitizer::Address),
+            "leak" => Ok(Sanitizer::Leak),
+            "memory" => Ok(Sanitizer::Memory),
+            "thread" => Ok(Sanitizer::Thread),
+            _ => Err(()),
+        }
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Hash)]
 pub enum OptLevel {
     No,         // -O0
@@ -816,6 +829,9 @@ macro_rules! options {
             Some("one of: `full`, `partial`, or `off`");
         pub const parse_sanitizer: Option<&str> =
             Some("one of: `address`, `leak`, `memory` or `thread`");
+        pub const parse_sanitizer_list: Option<&str> =
+            Some("comma separated list of sanitizers");
+        pub const parse_sanitizer_memory_track_origins: Option<&str> = None;
         pub const parse_linker_flavor: Option<&str> =
             Some(::rustc_target::spec::LinkerFlavor::one_of());
         pub const parse_optimization_fuel: Option<&str> =
@@ -1010,15 +1026,46 @@ macro_rules! options {
             true
         }
 
-        fn parse_sanitizer(slote: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
-            match v {
-                Some("address") => *slote = Some(Sanitizer::Address),
-                Some("leak") => *slote = Some(Sanitizer::Leak),
-                Some("memory") => *slote = Some(Sanitizer::Memory),
-                Some("thread") => *slote = Some(Sanitizer::Thread),
-                _ => return false,
+        fn parse_sanitizer(slot: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
+            if let Some(Ok(s)) =  v.map(str::parse) {
+                *slot = Some(s);
+                true
+            } else {
+                false
+            }
+        }
+
+        fn parse_sanitizer_list(slot: &mut Vec<Sanitizer>, v: Option<&str>) -> bool {
+            if let Some(v) = v {
+                for s in v.split(',').map(str::parse) {
+                    if let Ok(s) = s {
+                        if !slot.contains(&s) {
+                            slot.push(s);
+                        }
+                    } else {
+                        return false;
+                    }
+                }
+                true
+            } else {
+                false
+            }
+        }
+
+        fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool {
+            match v.map(|s| s.parse()) {
+                None => {
+                    *slot = 2;
+                    true
+                }
+                Some(Ok(i)) if i <= 2 => {
+                    *slot = i;
+                    true
+                }
+                _ => {
+                    false
+                }
             }
-            true
         }
 
         fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
@@ -1376,6 +1423,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "pass `-install_name @rpath/...` to the macOS linker"),
     sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
                                     "use a sanitizer"),
+    sanitizer_recover: Vec<Sanitizer> = (vec![], parse_sanitizer_list, [TRACKED],
+        "Enable recovery for selected sanitizers"),
+    sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
+        "Enable origins tracking in MemorySanitizer"),
     fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
         "set the optimization fuel quota for a crate"),
     print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
@@ -2881,6 +2932,7 @@ mod dep_tracking {
         Option<cstore::NativeLibraryKind>
     ));
     impl_dep_tracking_hash_for_sortable_vec_of!((String, u64));
+    impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer);
 
     impl<T1, T2> DepTrackingHash for (T1, T2)
     where
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs
index fa14ce7b03c..6cc7b0b4b1e 100644
--- a/src/librustc_codegen_llvm/back/write.rs
+++ b/src/librustc_codegen_llvm/back/write.rs
@@ -13,7 +13,7 @@ use crate::LlvmCodegenBackend;
 use rustc::hir::def_id::LOCAL_CRATE;
 use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig, run_assembler};
 use rustc_codegen_ssa::traits::*;
-use rustc::session::config::{self, OutputType, Passes, Lto, SwitchWithOptPath};
+use rustc::session::config::{self, OutputType, Passes, Lto, Sanitizer, SwitchWithOptPath};
 use rustc::session::Session;
 use rustc::ty::TyCtxt;
 use rustc_codegen_ssa::{RLIB_BYTECODE_EXTENSION, ModuleCodegen, CompiledModule};
@@ -29,7 +29,7 @@ use std::path::{Path, PathBuf};
 use std::str;
 use std::sync::Arc;
 use std::slice;
-use libc::{c_uint, c_void, c_char, size_t};
+use libc::{c_int, c_uint, c_void, c_char, size_t};
 
 pub const RELOC_MODEL_ARGS : [(&str, llvm::RelocMode); 7] = [
     ("pic", llvm::RelocMode::PIC),
@@ -323,7 +323,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
         llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
     }
 
-    if config.opt_level.is_some() {
+    if let Some(opt_level) = config.opt_level {
         // Create the two optimizing pass managers. These mirror what clang
         // does, and are by populated by LLVM's default PassManagerBuilder.
         // Each manager has a different set of passes, but they also share
@@ -363,6 +363,8 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
                 }
             }
 
+            add_sanitizer_passes(config, &mut extra_passes);
+
             for pass_name in &cgcx.plugin_passes {
                 if let Some(pass) = find_pass(pass_name) {
                     extra_passes.push(pass);
@@ -384,8 +386,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
             if !config.no_prepopulate_passes {
                 llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
                 llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
-                let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
-                    .unwrap_or(llvm::CodeGenOptLevel::None);
+                let opt_level = to_llvm_opt_settings(opt_level).0;
                 let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
                     (cgcx.lto != Lto::Fat && cgcx.opts.cg.linker_plugin_lto.enabled());
                 with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| {
@@ -449,6 +450,31 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
     Ok(())
 }
 
+unsafe fn add_sanitizer_passes(config: &ModuleConfig,
+                               passes: &mut Vec<&'static mut llvm::Pass>) {
+
+    let sanitizer = match &config.sanitizer {
+        None => return,
+        Some(s) => s,
+    };
+
+    let recover = config.sanitizer_recover.contains(sanitizer);
+    match sanitizer {
+        Sanitizer::Address => {
+            passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover));
+            passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover));
+        }
+        Sanitizer::Memory => {
+            let track_origins = config.sanitizer_memory_track_origins as c_int;
+            passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover));
+        }
+        Sanitizer::Thread => {
+            passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
+        }
+        Sanitizer::Leak => {}
+    }
+}
+
 pub(crate) unsafe fn codegen(cgcx: &CodegenContext<LlvmCodegenBackend>,
                   diag_handler: &Handler,
                   module: ModuleCodegen<ModuleLlvm>,
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index 548e8c732a7..241ca695e5f 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -1670,6 +1670,11 @@ extern "C" {
 
     pub fn LLVMRustPassKind(Pass: &Pass) -> PassKind;
     pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> Option<&'static mut Pass>;
+    pub fn LLVMRustCreateAddressSanitizerFunctionPass(Recover: bool) -> &'static mut Pass;
+    pub fn LLVMRustCreateModuleAddressSanitizerPass(Recover: bool) -> &'static mut Pass;
+    pub fn LLVMRustCreateMemorySanitizerPass(TrackOrigins: c_int,
+                                             Recover: bool) -> &'static mut Pass;
+    pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass;
     pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass);
     pub fn LLVMRustAddLastExtensionPasses(PMB: &PassManagerBuilder,
                                           Passes: *const &'static mut Pass,
diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs
index f35a31d59fe..9d3e57449f8 100644
--- a/src/librustc_codegen_ssa/back/write.rs
+++ b/src/librustc_codegen_ssa/back/write.rs
@@ -59,6 +59,10 @@ pub struct ModuleConfig {
     pub pgo_gen: SwitchWithOptPath,
     pub pgo_use: Option<PathBuf>,
 
+    pub sanitizer: Option<Sanitizer>,
+    pub sanitizer_recover: Vec<Sanitizer>,
+    pub sanitizer_memory_track_origins: usize,
+
     // Flags indicating which outputs to produce.
     pub emit_pre_lto_bc: bool,
     pub emit_no_opt_bc: bool,
@@ -97,6 +101,10 @@ impl ModuleConfig {
             pgo_gen: SwitchWithOptPath::Disabled,
             pgo_use: None,
 
+            sanitizer: None,
+            sanitizer_recover: Default::default(),
+            sanitizer_memory_track_origins: 0,
+
             emit_no_opt_bc: false,
             emit_pre_lto_bc: false,
             emit_bc: false,
@@ -345,29 +353,16 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
     let mut metadata_config = ModuleConfig::new(vec![]);
     let mut allocator_config = ModuleConfig::new(vec![]);
 
-    if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
-        match *sanitizer {
-            Sanitizer::Address => {
-                modules_config.passes.push("asan".to_owned());
-                modules_config.passes.push("asan-module".to_owned());
-            }
-            Sanitizer::Memory => {
-                modules_config.passes.push("msan".to_owned())
-            }
-            Sanitizer::Thread => {
-                modules_config.passes.push("tsan".to_owned())
-            }
-            _ => {}
-        }
-    }
-
     if sess.opts.debugging_opts.profile {
         modules_config.passes.push("insert-gcov-profiling".to_owned())
     }
 
     modules_config.pgo_gen = sess.opts.cg.profile_generate.clone();
     modules_config.pgo_use = sess.opts.cg.profile_use.clone();
-
+    modules_config.sanitizer = sess.opts.debugging_opts.sanitizer.clone();
+    modules_config.sanitizer_recover = sess.opts.debugging_opts.sanitizer_recover.clone();
+    modules_config.sanitizer_memory_track_origins =
+        sess.opts.debugging_opts.sanitizer_memory_track_origins;
     modules_config.opt_level = Some(sess.opts.optimize);
     modules_config.opt_size = Some(sess.opts.optimize);
 
diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp
index 3451346869d..062a8265cc6 100644
--- a/src/rustllvm/PassWrapper.cpp
+++ b/src/rustllvm/PassWrapper.cpp
@@ -20,9 +20,17 @@
 #include "llvm/Transforms/IPO/FunctionImport.h"
 #include "llvm/Transforms/Utils/FunctionImportUtils.h"
 #include "llvm/LTO/LTO.h"
-
 #include "llvm-c/Transforms/PassManagerBuilder.h"
 
+#include "llvm/Transforms/Instrumentation.h"
+#if LLVM_VERSION_GE(9, 0)
+#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+#endif
+#if LLVM_VERSION_GE(8, 0)
+#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
+#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
+#endif
+
 using namespace llvm;
 using namespace llvm::legacy;
 
@@ -76,6 +84,41 @@ extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
   return nullptr;
 }
 
+extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) {
+  const bool CompileKernel = false;
+
+  return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover));
+}
+
+extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) {
+  const bool CompileKernel = false;
+
+#if LLVM_VERSION_GE(9, 0)
+  return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover));
+#else
+  return wrap(createAddressSanitizerModulePass(CompileKernel, Recover));
+#endif
+}
+
+extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) {
+#if LLVM_VERSION_GE(8, 0)
+  const bool CompileKernel = false;
+
+  return wrap(createMemorySanitizerLegacyPassPass(
+      MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel}));
+#else
+  return wrap(createMemorySanitizerPass(TrackOrigins, Recover));
+#endif
+}
+
+extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
+#if LLVM_VERSION_GE(8, 0)
+  return wrap(createThreadSanitizerLegacyPassPass());
+#else
+  return wrap(createThreadSanitizerPass());
+#endif
+}
+
 extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
   assert(RustPass);
   Pass *Pass = unwrap(RustPass);
diff --git a/src/test/codegen/sanitizer-memory-track-orgins.rs b/src/test/codegen/sanitizer-memory-track-orgins.rs
new file mode 100644
index 00000000000..fd8be0bced7
--- /dev/null
+++ b/src/test/codegen/sanitizer-memory-track-orgins.rs
@@ -0,0 +1,28 @@
+// Verifies that MemorySanitizer track-origins level can be controlled
+// with -Zsanitizer-memory-track-origins option.
+//
+// needs-sanitizer-support
+// only-linux
+// only-x86_64
+// revisions:MSAN-0 MSAN-1 MSAN-2
+//
+//[MSAN-0] compile-flags: -Zsanitizer=memory
+//[MSAN-1] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins=1
+//[MSAN-2] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins
+
+#![crate_type="lib"]
+
+// MSAN-0-NOT: @__msan_track_origins
+// MSAN-1:     @__msan_track_origins = weak_odr local_unnamed_addr constant i32 1
+// MSAN-2:     @__msan_track_origins = weak_odr local_unnamed_addr constant i32 2
+//
+// MSAN-0-LABEL: define void @copy(
+// MSAN-1-LABEL: define void @copy(
+// MSAN-2-LABEL: define void @copy(
+#[no_mangle]
+pub fn copy(dst: &mut i32, src: &i32) {
+    // MSAN-0-NOT: call i32 @__msan_chain_origin(
+    // MSAN-1-NOT: call i32 @__msan_chain_origin(
+    // MSAN-2:     call i32 @__msan_chain_origin(
+    *dst = *src;
+}
diff --git a/src/test/codegen/sanitizer-recover.rs b/src/test/codegen/sanitizer-recover.rs
new file mode 100644
index 00000000000..a292332667b
--- /dev/null
+++ b/src/test/codegen/sanitizer-recover.rs
@@ -0,0 +1,34 @@
+// Verifies that AddressSanitizer and MemorySanitizer
+// recovery mode can be enabled with -Zsanitizer-recover.
+//
+// needs-sanitizer-support
+// only-linux
+// only-x86_64
+// revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER
+//
+//[ASAN]         compile-flags: -Zsanitizer=address
+//[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address
+//[MSAN]         compile-flags: -Zsanitizer=memory
+//[MSAN-RECOVER] compile-flags: -Zsanitizer=memory  -Zsanitizer-recover=memory
+
+#![crate_type="lib"]
+
+// ASAN-LABEL:         define i32 @penguin(
+// ASAN-RECOVER-LABEL: define i32 @penguin(
+// MSAN-LABEL:         define i32 @penguin(
+// MSAN-RECOVER-LABEL: define i32 @penguin(
+#[no_mangle]
+pub fn penguin(p: &mut i32) -> i32 {
+    // ASAN:             call void @__asan_report_load4(i64 %0)
+    // ASAN:             unreachable
+    //
+    // ASAN-RECOVER:     call void @__asan_report_load4_noabort(
+    // ASAN-RECOVER-NOT: unreachable
+    //
+    // MSAN:             call void @__msan_warning_noreturn()
+    // MSAN:             unreachable
+    //
+    // MSAN-RECOVER:     call void @__msan_warning()
+    // MSAN-RECOVER-NOT: unreachable
+    *p
+}