From 0a65f280c8eac844a74b758822ca52e8763a1d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 14 Jun 2020 00:00:00 +0000 Subject: [PATCH] Diagnose use of incompatible sanitizers Emit an error when incompatible sanitizer are configured through command line options. Previously the last one configured prevailed and others were silently ignored. Additionally use a set to represent configured sanitizers, making it possible to enable multiple sanitizers at once. At least in principle, since currently all of them are considered to be incompatible with others. --- Cargo.lock | 1 + .../src/compiler-flags/sanitizer.md | 3 +- src/librustc_codegen_llvm/attributes.rs | 45 +++++------ src/librustc_codegen_llvm/back/write.rs | 44 +++++------ src/librustc_codegen_llvm/base.rs | 6 +- src/librustc_codegen_llvm/llvm/ffi.rs | 7 +- src/librustc_codegen_ssa/back/link.rs | 40 +++++----- .../back/symbol_export.rs | 4 +- src/librustc_codegen_ssa/back/write.rs | 12 +-- src/librustc_interface/tests.rs | 8 +- .../middle/codegen_fn_attrs.rs | 17 ++--- src/librustc_mir/transform/inline.rs | 21 +----- src/librustc_session/Cargo.toml | 1 + src/librustc_session/config.rs | 75 ++++++++++++------- src/librustc_session/lib.rs | 3 + src/librustc_session/options.rs | 32 +++----- src/librustc_session/session.rs | 70 ++++++++--------- src/librustc_typeck/collect.rs | 9 ++- src/rustllvm/PassWrapper.cpp | 13 ++-- src/test/ui/sanitize/incompatible.rs | 6 ++ src/test/ui/sanitize/incompatible.stderr | 4 + src/test/ui/sanitize/unsupported-target.rs | 3 +- .../ui/sanitize/unsupported-target.stderr | 2 +- 23 files changed, 212 insertions(+), 214 deletions(-) create mode 100644 src/test/ui/sanitize/incompatible.rs create mode 100644 src/test/ui/sanitize/incompatible.stderr diff --git a/Cargo.lock b/Cargo.lock index bbbcf797f7535..80f4fc6091220 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4278,6 +4278,7 @@ dependencies = [ name = "rustc_session" version = "0.0.0" dependencies = [ + "bitflags", "getopts", "log", "num_cpus", diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 7ebd8054ba0b0..5e2e04c063bc4 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -12,8 +12,7 @@ This feature allows for use of one of following sanitizers: * [ThreadSanitizer][clang-tsan] a fast data race detector. To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`, -`-Zsanitizer=memory` or `-Zsanitizer=thread`. Only a single sanitizer can be -enabled at a time. +`-Zsanitizer=memory` or `-Zsanitizer=thread`. # AddressSanitizer diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index a4e17a5f675be..6234ade8a1612 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -11,7 +11,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::config::{OptLevel, Sanitizer}; +use rustc_session::config::{OptLevel, SanitizerSet}; use rustc_session::Session; use crate::attributes; @@ -45,26 +45,16 @@ fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) { /// Apply LLVM sanitize attributes. #[inline] -pub fn sanitize(cx: &CodegenCx<'ll, '_>, codegen_fn_flags: CodegenFnAttrFlags, llfn: &'ll Value) { - if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer { - match *sanitizer { - Sanitizer::Address => { - if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) { - llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); - } - } - Sanitizer::Memory => { - if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) { - llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); - } - } - Sanitizer::Thread => { - if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) { - llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); - } - } - Sanitizer::Leak => {} - } +pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll Value) { + let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize; + if enabled.contains(SanitizerSet::ADDRESS) { + llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); + } + if enabled.contains(SanitizerSet::MEMORY) { + llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); + } + if enabled.contains(SanitizerSet::THREAD) { + llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); } } @@ -123,9 +113,14 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { // Currently stack probes seem somewhat incompatible with the address // sanitizer and thread sanitizer. With asan we're already protected from // stack overflow anyway so we don't really need stack probes regardless. - match cx.sess().opts.debugging_opts.sanitizer { - Some(Sanitizer::Address | Sanitizer::Thread) => return, - _ => {} + if cx + .sess() + .opts + .debugging_opts + .sanitizer + .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) + { + return; } // probestack doesn't play nice either with `-C profile-generate`. @@ -296,7 +291,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); } - sanitize(cx, codegen_fn_attrs.flags, llfn); + sanitize(cx, codegen_fn_attrs.no_sanitize, llfn); // Always annotate functions with the target-cpu they are compiled for. // Without this, ThinLTO won't inline Rust functions into Clang generated diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 26f5334668b8f..868ce876a8192 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -21,7 +21,7 @@ use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, Lto, OutputType, Passes, Sanitizer, SwitchWithOptPath}; +use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::InnerSpan; use rustc_target::spec::{CodeModel, RelocModel}; @@ -394,12 +394,13 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO; // Sanitizer instrumentation is only inserted during the pre-link optimization stage. let sanitizer_options = if !is_lto { - config.sanitizer.as_ref().map(|s| llvm::SanitizerOptions { - sanitize_memory: *s == Sanitizer::Memory, - sanitize_thread: *s == Sanitizer::Thread, - sanitize_address: *s == Sanitizer::Address, - sanitize_recover: config.sanitizer_recover.contains(s), + Some(llvm::SanitizerOptions { + sanitize_address: config.sanitizer.contains(SanitizerSet::ADDRESS), + sanitize_address_recover: config.sanitizer_recover.contains(SanitizerSet::ADDRESS), + sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY), + sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY), sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int, + sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), }) } else { None @@ -600,25 +601,18 @@ pub(crate) unsafe fn optimize( } 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 => {} + if config.sanitizer.contains(SanitizerSet::ADDRESS) { + let recover = config.sanitizer_recover.contains(SanitizerSet::ADDRESS); + passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover)); + passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover)); + } + if config.sanitizer.contains(SanitizerSet::MEMORY) { + let track_origins = config.sanitizer_memory_track_origins as c_int; + let recover = config.sanitizer_recover.contains(SanitizerSet::MEMORY); + passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover)); + } + if config.sanitizer.contains(SanitizerSet::THREAD) { + passes.push(llvm::LLVMRustCreateThreadSanitizerPass()); } } diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 3e17a51528e3e..e99fb8dcae1e5 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -29,12 +29,12 @@ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_middle::dep_graph; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::middle::exported_symbols; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::TyCtxt; -use rustc_session::config::DebugInfo; +use rustc_session::config::{DebugInfo, SanitizerSet}; use rustc_span::symbol::Symbol; use std::ffi::CString; @@ -132,7 +132,7 @@ pub fn compile_codegen_unit( // If this codegen unit contains the main function, also create the // wrapper here if let Some(entry) = maybe_create_entry_wrapper::>(&cx) { - attributes::sanitize(&cx, CodegenFnAttrFlags::empty(), entry); + attributes::sanitize(&cx, SanitizerSet::empty(), entry); } // Run replace-all-uses-with for statics that need it diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 54cf99e1c6d6c..225e220df63b8 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -439,11 +439,12 @@ pub enum OptStage { /// LLVMRustSanitizerOptions #[repr(C)] pub struct SanitizerOptions { - pub sanitize_memory: bool, - pub sanitize_thread: bool, pub sanitize_address: bool, - pub sanitize_recover: bool, + pub sanitize_address_recover: bool, + pub sanitize_memory: bool, + pub sanitize_memory_recover: bool, pub sanitize_memory_track_origins: c_int, + pub sanitize_thread: bool, } /// LLVMRelocMode diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 53e3da3c0baf0..b39c72d95d7bc 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -4,7 +4,7 @@ use rustc_hir::def_id::CrateNum; use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLib}; use rustc_middle::middle::dependency_format::Linkage; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo}; -use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, Sanitizer}; +use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SanitizerSet}; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; @@ -766,23 +766,26 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( } } -fn link_sanitizer_runtime(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) { - let sanitizer = match &sess.opts.debugging_opts.sanitizer { - Some(s) => s, - None => return, - }; - +fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) { if crate_type != CrateType::Executable { return; } + let sanitizer = sess.opts.debugging_opts.sanitizer; + if sanitizer.contains(SanitizerSet::ADDRESS) { + link_sanitizer_runtime(sess, linker, "asan"); + } + if sanitizer.contains(SanitizerSet::LEAK) { + link_sanitizer_runtime(sess, linker, "lsan"); + } + if sanitizer.contains(SanitizerSet::MEMORY) { + link_sanitizer_runtime(sess, linker, "msan"); + } + if sanitizer.contains(SanitizerSet::THREAD) { + link_sanitizer_runtime(sess, linker, "tsan"); + } +} - let name = match sanitizer { - Sanitizer::Address => "asan", - Sanitizer::Leak => "lsan", - Sanitizer::Memory => "msan", - Sanitizer::Thread => "tsan", - }; - +fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { let default_sysroot = filesearch::get_or_default_sysroot(); let default_tlib = filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple()); @@ -1548,9 +1551,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER if sess.target.target.options.is_like_fuchsia && crate_type == CrateType::Executable { - let prefix = match sess.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => "asan/", - _ => "", + let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) { + "asan/" + } else { + "" }; cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); } @@ -1574,7 +1578,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // OBJECT-FILES-YES, AUDIT-ORDER - link_sanitizer_runtime(sess, crate_type, cmd); + link_sanitizers(sess, crate_type, cmd); // OBJECT-FILES-NO, AUDIT-ORDER // Linker plugins should be specified early in the list of arguments diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs index 970d13b30c04e..b3f75a8b1c03d 100644 --- a/src/librustc_codegen_ssa/back/symbol_export.rs +++ b/src/librustc_codegen_ssa/back/symbol_export.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::Instance; use rustc_middle::ty::{SymbolName, TyCtxt}; -use rustc_session::config::{CrateType, Sanitizer}; +use rustc_session::config::{CrateType, SanitizerSet}; pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { crates_export_threshold(&tcx.sess.crate_types()) @@ -202,7 +202,7 @@ fn exported_symbols_provider_local( })); } - if let Some(Sanitizer::Memory) = tcx.sess.opts.debugging_opts.sanitizer { + if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) { // Similar to profiling, preserve weak msan symbol during LTO. const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"]; diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index c118e5ebdb72d..7efdd5c67aba8 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -29,7 +29,7 @@ use rustc_middle::middle::exported_symbols::SymbolExportLevel; use rustc_middle::ty::TyCtxt; use rustc_session::cgu_reuse_tracker::CguReuseTracker; use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; -use rustc_session::config::{Passes, Sanitizer, SwitchWithOptPath}; +use rustc_session::config::{Passes, SanitizerSet, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{sym, Symbol}; @@ -86,8 +86,8 @@ pub struct ModuleConfig { pub pgo_gen: SwitchWithOptPath, pub pgo_use: Option, - pub sanitizer: Option, - pub sanitizer_recover: Vec, + pub sanitizer: SanitizerSet, + pub sanitizer_recover: SanitizerSet, pub sanitizer_memory_track_origins: usize, // Flags indicating which outputs to produce. @@ -189,10 +189,10 @@ impl ModuleConfig { ), pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None), - sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer.clone(), None), + sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()), sanitizer_recover: if_regular!( - sess.opts.debugging_opts.sanitizer_recover.clone(), - vec![] + sess.opts.debugging_opts.sanitizer_recover, + SanitizerSet::empty() ), sanitizer_memory_track_origins: if_regular!( sess.opts.debugging_opts.sanitizer_memory_track_origins, diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 87647f3b0b017..7e3ba0da709ae 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -6,7 +6,9 @@ use rustc_session::config::Strip; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; -use rustc_session::config::{Externs, OutputType, OutputTypes, Sanitizer, SymbolManglingVersion}; +use rustc_session::config::{ + Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, +}; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_session::utils::NativeLibKind; @@ -568,9 +570,9 @@ fn test_debugging_options_tracking_hash() { tracked!(relro_level, Some(RelroLevel::Full)); tracked!(report_delayed_bugs, true); tracked!(run_dsymutil, false); - tracked!(sanitizer, Some(Sanitizer::Address)); + tracked!(sanitizer, SanitizerSet::ADDRESS); tracked!(sanitizer_memory_track_origins, 2); - tracked!(sanitizer_recover, vec![Sanitizer::Address]); + tracked!(sanitizer_recover, SanitizerSet::ADDRESS); tracked!(saturating_float_casts, Some(true)); tracked!(share_generics, Some(true)); tracked!(show_span, Some(String::from("abc"))); diff --git a/src/librustc_middle/middle/codegen_fn_attrs.rs b/src/librustc_middle/middle/codegen_fn_attrs.rs index c480944069efb..d2749f8529bed 100644 --- a/src/librustc_middle/middle/codegen_fn_attrs.rs +++ b/src/librustc_middle/middle/codegen_fn_attrs.rs @@ -1,5 +1,6 @@ use crate::mir::mono::Linkage; use rustc_attr::{InlineAttr, OptimizeAttr}; +use rustc_session::config::SanitizerSet; use rustc_span::symbol::Symbol; #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] @@ -30,6 +31,9 @@ pub struct CodegenFnAttrs { /// The `#[link_section = "..."]` attribute, or what executable section this /// should be placed in. pub link_section: Option, + /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which + /// instrumentation should be disabled inside the annotated function. + pub no_sanitize: SanitizerSet, } bitflags! { @@ -69,20 +73,12 @@ bitflags! { const FFI_RETURNS_TWICE = 1 << 10; /// `#[track_caller]`: allow access to the caller location const TRACK_CALLER = 1 << 11; - /// `#[no_sanitize(address)]`: disables address sanitizer instrumentation - const NO_SANITIZE_ADDRESS = 1 << 12; - /// `#[no_sanitize(memory)]`: disables memory sanitizer instrumentation - const NO_SANITIZE_MEMORY = 1 << 13; - /// `#[no_sanitize(thread)]`: disables thread sanitizer instrumentation - const NO_SANITIZE_THREAD = 1 << 14; - /// All `#[no_sanitize(...)]` attributes. - const NO_SANITIZE_ANY = Self::NO_SANITIZE_ADDRESS.bits | Self::NO_SANITIZE_MEMORY.bits | Self::NO_SANITIZE_THREAD.bits; /// #[ffi_pure]: applies clang's `pure` attribute to a foreign function /// declaration. - const FFI_PURE = 1 << 15; + const FFI_PURE = 1 << 12; /// #[ffi_const]: applies clang's `const` attribute to a foreign function /// declaration. - const FFI_CONST = 1 << 16; + const FFI_CONST = 1 << 13; } } @@ -98,6 +94,7 @@ impl CodegenFnAttrs { target_features: vec![], linkage: None, link_section: None, + no_sanitize: SanitizerSet::empty(), } } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 47aa4fbf60c03..9057281c3118c 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -9,7 +9,6 @@ use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; -use rustc_session::config::Sanitizer; use rustc_target::spec::abi::Abi; use super::simplify::{remove_dead_blocks, CfgSimplifier}; @@ -232,24 +231,8 @@ impl Inliner<'tcx> { // Avoid inlining functions marked as no_sanitize if sanitizer is enabled, // since instrumentation might be enabled and performed on the caller. - match self.tcx.sess.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => { - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) { - return false; - } - } - Some(Sanitizer::Memory) => { - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) { - return false; - } - } - Some(Sanitizer::Thread) => { - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) { - return false; - } - } - Some(Sanitizer::Leak) => {} - None => {} + if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) { + return false; } let hinted = match codegen_fn_attrs.inline { diff --git a/src/librustc_session/Cargo.toml b/src/librustc_session/Cargo.toml index 4d57c6384ddbd..abce7359c0ed7 100644 --- a/src/librustc_session/Cargo.toml +++ b/src/librustc_session/Cargo.toml @@ -9,6 +9,7 @@ name = "rustc_session" path = "lib.rs" [dependencies] +bitflags = "1.2.1" getopts = "0.2" log = "0.4" rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs index 411a6eecbba15..53cec04083696 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -10,6 +10,7 @@ use crate::{early_error, early_warn, Session}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::impl_stable_hash_via_hash; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_target::spec::{Target, TargetTriple}; @@ -37,35 +38,55 @@ pub struct Config { pub ptr_width: u32, } -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum Sanitizer { - Address, - Leak, - Memory, - Thread, +bitflags! { + #[derive(Default, RustcEncodable, RustcDecodable)] + pub struct SanitizerSet: u8 { + const ADDRESS = 1 << 0; + const LEAK = 1 << 1; + const MEMORY = 1 << 2; + const THREAD = 1 << 3; + } } -impl fmt::Display for Sanitizer { +/// Formats a sanitizer set as a comma separated list of sanitizers' names. +impl fmt::Display for SanitizerSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Sanitizer::Address => "address".fmt(f), - Sanitizer::Leak => "leak".fmt(f), - Sanitizer::Memory => "memory".fmt(f), - Sanitizer::Thread => "thread".fmt(f), + let mut first = true; + for s in *self { + let name = match s { + SanitizerSet::ADDRESS => "address", + SanitizerSet::LEAK => "leak", + SanitizerSet::MEMORY => "memory", + SanitizerSet::THREAD => "thread", + _ => panic!("unrecognized sanitizer {:?}", s), + }; + if !first { + f.write_str(",")?; + } + f.write_str(name)?; + first = false; } + Ok(()) } } -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(()), - } +impl IntoIterator for SanitizerSet { + type Item = SanitizerSet; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] + .iter() + .copied() + .filter(|&s| self.contains(s)) + .collect::>() + .into_iter() + } +} + +impl HashStable for SanitizerSet { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.bits().hash_stable(ctx, hasher); } } @@ -726,10 +747,12 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { } } } - if let Some(s) = &sess.opts.debugging_opts.sanitizer { + + for s in sess.opts.debugging_opts.sanitizer { let symbol = Symbol::intern(&s.to_string()); ret.insert((sym::sanitize, Some(symbol))); } + if sess.opts.debug_assertions { ret.insert((Symbol::intern("debug_assertions"), None)); } @@ -1995,7 +2018,7 @@ impl PpMode { crate mod dep_tracking { use super::{ CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, - OutputTypes, Passes, Sanitizer, SourceFileHashAlgorithm, SwitchWithOptPath, + OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion, }; use crate::lint; @@ -2069,8 +2092,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(UnstableFeatures); impl_dep_tracking_hash_via_hash!(OutputTypes); impl_dep_tracking_hash_via_hash!(NativeLibKind); - impl_dep_tracking_hash_via_hash!(Sanitizer); - impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(SanitizerSet); impl_dep_tracking_hash_via_hash!(CFGuard); impl_dep_tracking_hash_via_hash!(TargetTriple); impl_dep_tracking_hash_via_hash!(Edition); @@ -2085,7 +2107,6 @@ crate mod dep_tracking { impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); impl_dep_tracking_hash_for_sortable_vec_of!((String, Option, NativeLibKind)); 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_session/lib.rs b/src/librustc_session/lib.rs index 80b693fe1ab60..be9d2e7be2777 100644 --- a/src/librustc_session/lib.rs +++ b/src/librustc_session/lib.rs @@ -1,6 +1,9 @@ #![feature(crate_visibility_modifier)] #![feature(or_patterns)] +#[macro_use] +extern crate bitflags; + pub mod cgu_reuse_tracker; pub mod utils; #[macro_use] diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index d22c6ec9d7d01..868db7e2a2e0f 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -248,8 +248,7 @@ macro_rules! options { pub const parse_passes: &str = "a space-separated list of passes, or `all`"; pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizer: &str = "one of: `address`, `leak`, `memory` or `thread`"; - pub const parse_sanitizer_list: &str = "comma separated list of sanitizers"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either `disabled`, `nochecks`, or `checks`"; pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; @@ -459,24 +458,15 @@ macro_rules! options { true } - 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 { + fn parse_sanitizers(slot: &mut SanitizerSet, 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; + for s in v.split(',') { + *slot |= match s { + "address" => SanitizerSet::ADDRESS, + "leak" => SanitizerSet::LEAK, + "memory" => SanitizerSet::MEMORY, + "thread" => SanitizerSet::THREAD, + _ => return false, } } true @@ -971,11 +961,11 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, // soon. run_dsymutil: bool = (true, parse_bool, [TRACKED], "if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"), - sanitizer: Option = (None, parse_sanitizer, [TRACKED], + sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], "use a sanitizer"), sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], "enable origins tracking in MemorySanitizer"), - sanitizer_recover: Vec = (vec![], parse_sanitizer_list, [TRACKED], + sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], "enable recovery for selected sanitizers"), saturating_float_casts: Option = (None, parse_opt_bool, [TRACKED], "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index 06d7d4f14d8f4..90cd6ae5bf99f 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -1,7 +1,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, CrateType, OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; +use crate::config::{self, CrateType, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath}; use crate::filesearch; use crate::lint; use crate::parse::ParseSess; @@ -650,14 +650,9 @@ impl Session { } pub fn fewer_names(&self) -> bool { let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) - || self.opts.output_types.contains_key(&OutputType::Bitcode); - - // Address sanitizer and memory sanitizer use alloca name when reporting an issue. - let more_names = match self.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => true, - Some(Sanitizer::Memory) => true, - _ => more_names, - }; + || self.opts.output_types.contains_key(&OutputType::Bitcode) + // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); self.opts.debugging_opts.fewer_names || !more_names } @@ -1020,12 +1015,10 @@ impl Session { /// Checks if LLVM lifetime markers should be emitted. pub fn emit_lifetime_markers(&self) -> bool { - match self.opts.debugging_opts.sanitizer { - // AddressSanitizer uses lifetimes to detect use after scope bugs. - // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - Some(Sanitizer::Address | Sanitizer::Memory) => true, - _ => self.opts.optimize != config::OptLevel::No, - } + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) } } @@ -1356,33 +1349,36 @@ fn validate_commandline_args_with_session_available(sess: &Session) { ); } + const ASAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-fuchsia", "x86_64-apple-darwin", "x86_64-fuchsia", "x86_64-unknown-linux-gnu"]; + const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"]; + const TSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + // 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", - "x86_64-fuchsia", - "aarch64-fuchsia", - ]; - 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, + for s in sess.opts.debugging_opts.sanitizer { + let supported_targets = match s { + SanitizerSet::ADDRESS => ASAN_SUPPORTED_TARGETS, + SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS, + SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS, + SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS, + _ => panic!("unrecognized sanitizer {}", s), }; - if !supported_targets.contains(&&*sess.opts.target_triple.triple()) { sess.err(&format!( - "{:?}Sanitizer only works with the `{}` target", - sanitizer, - supported_targets.join("` or `") + "`-Zsanitizer={}` only works with targets: {}", + s, + supported_targets.join(", ") + )); + } + let conflicting = sess.opts.debugging_opts.sanitizer - s; + if !conflicting.is_empty() { + sess.err(&format!( + "`-Zsanitizer={}` is incompatible with `-Zsanitizer={}`", + s, conflicting, )); + // Don't report additional errors. + break; } } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 355b4fc413f42..8a2286fb737bb 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -40,6 +40,7 @@ use rustc_middle::ty::util::Discr; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; +use rustc_session::config::SanitizerSet; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -2488,11 +2489,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { if let Some(list) = attr.meta_item_list() { for item in list.iter() { if item.check_name(sym::address) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_ADDRESS; + codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; } else if item.check_name(sym::memory) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_MEMORY; + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; } else if item.check_name(sym::thread) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_THREAD; + codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") @@ -2592,7 +2593,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } } - if codegen_fn_attrs.flags.intersects(CodegenFnAttrFlags::NO_SANITIZE_ANY) { + if !codegen_fn_attrs.no_sanitize.is_empty() { if codegen_fn_attrs.inline == InlineAttr::Always { if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { let hir_id = tcx.hir().as_local_hir_id(id.expect_local()); diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 3d252fe70afeb..8c0fd0e7eabbc 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -711,11 +711,12 @@ enum class LLVMRustOptStage { }; struct LLVMRustSanitizerOptions { + bool SanitizeAddress; + bool SanitizeAddressRecover; bool SanitizeMemory; + bool SanitizeMemoryRecover; + int SanitizeMemoryTrackOrigins; bool SanitizeThread; - bool SanitizeAddress; - bool SanitizeRecover; - int SanitizeMemoryTrackOrigins; }; extern "C" void @@ -802,7 +803,7 @@ LLVMRustOptimizeWithNewPassManager( if (SanitizerOptions->SanitizeMemory) { MemorySanitizerOptions Options( SanitizerOptions->SanitizeMemoryTrackOrigins, - SanitizerOptions->SanitizeRecover, + SanitizerOptions->SanitizeMemoryRecover, /*CompileKernel=*/false); #if LLVM_VERSION_GE(10, 0) PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) { @@ -836,14 +837,14 @@ LLVMRustOptimizeWithNewPassManager( OptimizerLastEPCallbacks.push_back( [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { FPM.addPass(AddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeRecover, + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, /*UseAfterScope=*/true)); } ); PipelineStartEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM) { MPM.addPass(ModuleAddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeRecover)); + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); } ); } diff --git a/src/test/ui/sanitize/incompatible.rs b/src/test/ui/sanitize/incompatible.rs new file mode 100644 index 0000000000000..4947f3b3d8b13 --- /dev/null +++ b/src/test/ui/sanitize/incompatible.rs @@ -0,0 +1,6 @@ +// compile-flags: -Z sanitizer=address -Z sanitizer=memory --target x86_64-unknown-linux-gnu +// error-pattern: error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory` + +#![feature(no_core)] +#![no_core] +#![no_main] diff --git a/src/test/ui/sanitize/incompatible.stderr b/src/test/ui/sanitize/incompatible.stderr new file mode 100644 index 0000000000000..f86db41bac717 --- /dev/null +++ b/src/test/ui/sanitize/incompatible.stderr @@ -0,0 +1,4 @@ +error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory` + +error: aborting due to previous error + diff --git a/src/test/ui/sanitize/unsupported-target.rs b/src/test/ui/sanitize/unsupported-target.rs index 444333c3f01e2..6ccc9988cdecc 100644 --- a/src/test/ui/sanitize/unsupported-target.rs +++ b/src/test/ui/sanitize/unsupported-target.rs @@ -1,6 +1,5 @@ -// ignore-tidy-linelength // compile-flags: -Z sanitizer=leak --target i686-unknown-linux-gnu -// error-pattern: error: LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target +// error-pattern: error: `-Zsanitizer=leak` only works with targets: #![feature(no_core)] #![no_core] diff --git a/src/test/ui/sanitize/unsupported-target.stderr b/src/test/ui/sanitize/unsupported-target.stderr index 38be58dd4b365..f9434bc9512d0 100644 --- a/src/test/ui/sanitize/unsupported-target.stderr +++ b/src/test/ui/sanitize/unsupported-target.stderr @@ -1,4 +1,4 @@ -error: LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target +error: `-Zsanitizer=leak` only works with targets: x86_64-apple-darwin, x86_64-unknown-linux-gnu error: aborting due to previous error