name = "rustc_session"
version = "0.0.0"
dependencies = [
+ "bitflags",
"getopts",
"log",
"num_cpus",
* [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
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;
/// 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);
}
}
// 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`.
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
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};
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
}
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());
}
}
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;
// If this codegen unit contains the main function, also create the
// wrapper here
if let Some(entry) = maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx) {
- attributes::sanitize(&cx, CodegenFnAttrFlags::empty(), entry);
+ attributes::sanitize(&cx, SanitizerSet::empty(), entry);
}
// Run replace-all-uses-with for statics that need it
/// 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
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;
}
}
-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());
// 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));
}
}
// 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
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())
}));
}
- 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"];
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};
pub pgo_gen: SwitchWithOptPath,
pub pgo_use: Option<PathBuf>,
- pub sanitizer: Option<Sanitizer>,
- pub sanitizer_recover: Vec<Sanitizer>,
+ pub sanitizer: SanitizerSet,
+ pub sanitizer_recover: SanitizerSet,
pub sanitizer_memory_track_origins: usize,
// Flags indicating which outputs to produce.
),
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,
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;
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")));
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)]
/// The `#[link_section = "..."]` attribute, or what executable section this
/// should be placed in.
pub link_section: Option<Symbol>,
+ /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which
+ /// instrumentation should be disabled inside the annotated function.
+ pub no_sanitize: SanitizerSet,
}
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;
}
}
target_features: vec![],
linkage: None,
link_section: None,
+ no_sanitize: SanitizerSet::empty(),
}
}
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};
// 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 {
path = "lib.rs"
[dependencies]
+bitflags = "1.2.1"
getopts = "0.2"
log = "0.4"
rustc_errors = { path = "../librustc_errors" }
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};
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<Sanitizer, ()> {
- 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<SanitizerSet>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD]
+ .iter()
+ .copied()
+ .filter(|&s| self.contains(s))
+ .collect::<Vec<_>>()
+ .into_iter()
+ }
+}
+
+impl<CTX> HashStable<CTX> for SanitizerSet {
+ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
+ self.bits().hash_stable(ctx, hasher);
}
}
}
}
}
- 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));
}
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;
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<Sanitizer>);
+ 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);
impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level));
impl_dep_tracking_hash_for_sortable_vec_of!((String, Option<String>, NativeLibKind));
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
#![feature(crate_visibility_modifier)]
#![feature(or_patterns)]
+#[macro_use]
+extern crate bitflags;
+
pub mod cgu_reuse_tracker;
pub mod utils;
#[macro_use]
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`";
true
}
- 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 {
+ 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
// soon.
run_dsymutil: bool = (true, parse_bool, [TRACKED],
"if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"),
- sanitizer: Option<Sanitizer> = (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<Sanitizer> = (vec![], parse_sanitizer_list, [TRACKED],
+ sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
"enable recovery for selected sanitizers"),
saturating_float_casts: Option<bool> = (None, parse_opt_bool, [TRACKED],
"make float->int casts UB-free: numbers outside the integer type's range are clipped to \
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;
}
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
}
/// 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)
}
}
);
}
+ 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;
}
}
}
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};
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`")
}
}
- 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());
};
struct LLVMRustSanitizerOptions {
+ bool SanitizeAddress;
+ bool SanitizeAddressRecover;
bool SanitizeMemory;
+ bool SanitizeMemoryRecover;
+ int SanitizeMemoryTrackOrigins;
bool SanitizeThread;
- bool SanitizeAddress;
- bool SanitizeRecover;
- int SanitizeMemoryTrackOrigins;
};
extern "C" void
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) {
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));
}
);
}
--- /dev/null
+// 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]
--- /dev/null
+error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory`
+
+error: aborting due to previous error
+
-// 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]
-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