Skip to content

Commit

Permalink
Diagnose use of incompatible sanitizers
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tmiasko committed Jun 14, 2020
1 parent 4fb54ed commit 0a65f28
Show file tree
Hide file tree
Showing 23 changed files with 212 additions and 214 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Expand Up @@ -4278,6 +4278,7 @@ dependencies = [
name = "rustc_session"
version = "0.0.0"
dependencies = [
"bitflags",
"getopts",
"log",
"num_cpus",
Expand Down
3 changes: 1 addition & 2 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Expand Up @@ -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

Expand Down
45 changes: 20 additions & 25 deletions src/librustc_codegen_llvm/attributes.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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
Expand Down
44 changes: 19 additions & 25 deletions src/librustc_codegen_llvm/back/write.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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());
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/librustc_codegen_llvm/base.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<Builder<'_, '_, '_>>(&cx) {
attributes::sanitize(&cx, CodegenFnAttrFlags::empty(), entry);
attributes::sanitize(&cx, SanitizerSet::empty(), entry);
}

// Run replace-all-uses-with for statics that need it
Expand Down
7 changes: 4 additions & 3 deletions src/librustc_codegen_llvm/llvm/ffi.rs
Expand Up @@ -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
Expand Down
40 changes: 22 additions & 18 deletions src/librustc_codegen_ssa/back/link.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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));
}
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_ssa/back/symbol_export.rs
Expand Up @@ -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())
Expand Down Expand Up @@ -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"];

Expand Down
12 changes: 6 additions & 6 deletions src/librustc_codegen_ssa/back/write.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -86,8 +86,8 @@ pub struct ModuleConfig {
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.
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 5 additions & 3 deletions src/librustc_interface/tests.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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")));
Expand Down
17 changes: 7 additions & 10 deletions 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)]
Expand Down Expand Up @@ -30,6 +31,9 @@ pub struct CodegenFnAttrs {
/// 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! {
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -98,6 +94,7 @@ impl CodegenFnAttrs {
target_features: vec![],
linkage: None,
link_section: None,
no_sanitize: SanitizerSet::empty(),
}
}

Expand Down

0 comments on commit 0a65f28

Please sign in to comment.