diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 50752bac30f57..66c73d138e241 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -43,7 +43,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, @@ -51,6 +51,19 @@ pub enum Sanitizer { Thread, } +impl FromStr for Sanitizer { + type Err = (); + fn from_str(s: &str) -> Result { + 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 @@ -819,6 +832,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> = @@ -1013,15 +1029,46 @@ macro_rules! options { true } - fn parse_sanitizer(slote: &mut Option, 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, 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, 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, v: Option<&str>) -> bool { @@ -1379,6 +1426,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "pass `-install_name @rpath/...` to the macOS linker"), sanitizer: Option = (None, parse_sanitizer, [TRACKED], "use a sanitizer"), + sanitizer_recover: Vec = (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 = (None, parse_opt_string, [TRACKED], @@ -2984,6 +3035,7 @@ mod dep_tracking { Option )); impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); + impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer); impl DepTrackingHash for (T1, T2) where diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index fa14ce7b03c8c..6cc7b0b4b1e42 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, 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, } } + 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, 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, 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, diag_handler: &Handler, module: ModuleCodegen, diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 548e8c732a737..241ca695e5fc3 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 f35a31d59fe62..9d3e57449f899 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, + pub sanitizer: Option, + pub sanitizer_recover: Vec, + 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( 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 3451346869df9..062a8265cc6b7 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 0000000000000..a94b430d4a6f4 --- /dev/null +++ b/src/test/codegen/sanitizer-memory-track-orgins.rs @@ -0,0 +1,26 @@ +// Verifies that MemorySanitizer track-origins level can be controlled +// with -Zsanitizer-memory-track-origins option. +// +// needs-sanitizer-support +// 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 0000000000000..93540e9e0f245 --- /dev/null +++ b/src/test/codegen/sanitizer-recover.rs @@ -0,0 +1,32 @@ +// Verifies that AddressSanitizer and MemorySanitizer +// recovery mode can be enabled with -Zsanitizer-recover. +// +// needs-sanitizer-support +// 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 +}