Skip to content

Commit 17b80d9

Browse files
authored
Rollup merge of #73347 - tmiasko:incompatible-sanitizers, r=nikic
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.
2 parents 2d1bd57 + 0a65f28 commit 17b80d9

File tree

23 files changed

+212
-214
lines changed

23 files changed

+212
-214
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4278,6 +4278,7 @@ dependencies = [
42784278
name = "rustc_session"
42794279
version = "0.0.0"
42804280
dependencies = [
4281+
"bitflags",
42814282
"getopts",
42824283
"log",
42834284
"num_cpus",

src/doc/unstable-book/src/compiler-flags/sanitizer.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ This feature allows for use of one of following sanitizers:
1212
* [ThreadSanitizer][clang-tsan] a fast data race detector.
1313

1414
To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`,
15-
`-Zsanitizer=memory` or `-Zsanitizer=thread`. Only a single sanitizer can be
16-
enabled at a time.
15+
`-Zsanitizer=memory` or `-Zsanitizer=thread`.
1716

1817
# AddressSanitizer
1918

src/librustc_codegen_llvm/attributes.rs

+20-25
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1111
use rustc_middle::ty::layout::HasTyCtxt;
1212
use rustc_middle::ty::query::Providers;
1313
use rustc_middle::ty::{self, TyCtxt};
14-
use rustc_session::config::{OptLevel, Sanitizer};
14+
use rustc_session::config::{OptLevel, SanitizerSet};
1515
use rustc_session::Session;
1616

1717
use crate::attributes;
@@ -45,26 +45,16 @@ fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
4545

4646
/// Apply LLVM sanitize attributes.
4747
#[inline]
48-
pub fn sanitize(cx: &CodegenCx<'ll, '_>, codegen_fn_flags: CodegenFnAttrFlags, llfn: &'ll Value) {
49-
if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer {
50-
match *sanitizer {
51-
Sanitizer::Address => {
52-
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) {
53-
llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
54-
}
55-
}
56-
Sanitizer::Memory => {
57-
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) {
58-
llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
59-
}
60-
}
61-
Sanitizer::Thread => {
62-
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) {
63-
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
64-
}
65-
}
66-
Sanitizer::Leak => {}
67-
}
48+
pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll Value) {
49+
let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize;
50+
if enabled.contains(SanitizerSet::ADDRESS) {
51+
llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
52+
}
53+
if enabled.contains(SanitizerSet::MEMORY) {
54+
llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
55+
}
56+
if enabled.contains(SanitizerSet::THREAD) {
57+
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
6858
}
6959
}
7060

@@ -123,9 +113,14 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
123113
// Currently stack probes seem somewhat incompatible with the address
124114
// sanitizer and thread sanitizer. With asan we're already protected from
125115
// stack overflow anyway so we don't really need stack probes regardless.
126-
match cx.sess().opts.debugging_opts.sanitizer {
127-
Some(Sanitizer::Address | Sanitizer::Thread) => return,
128-
_ => {}
116+
if cx
117+
.sess()
118+
.opts
119+
.debugging_opts
120+
.sanitizer
121+
.intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
122+
{
123+
return;
129124
}
130125

131126
// 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::
296291
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
297292
Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
298293
}
299-
sanitize(cx, codegen_fn_attrs.flags, llfn);
294+
sanitize(cx, codegen_fn_attrs.no_sanitize, llfn);
300295

301296
// Always annotate functions with the target-cpu they are compiled for.
302297
// Without this, ThinLTO won't inline Rust functions into Clang generated

src/librustc_codegen_llvm/back/write.rs

+19-25
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_fs_util::{link_or_copy, path_to_c_string};
2121
use rustc_hir::def_id::LOCAL_CRATE;
2222
use rustc_middle::bug;
2323
use rustc_middle::ty::TyCtxt;
24-
use rustc_session::config::{self, Lto, OutputType, Passes, Sanitizer, SwitchWithOptPath};
24+
use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath};
2525
use rustc_session::Session;
2626
use rustc_span::InnerSpan;
2727
use rustc_target::spec::{CodeModel, RelocModel};
@@ -394,12 +394,13 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
394394
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
395395
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
396396
let sanitizer_options = if !is_lto {
397-
config.sanitizer.as_ref().map(|s| llvm::SanitizerOptions {
398-
sanitize_memory: *s == Sanitizer::Memory,
399-
sanitize_thread: *s == Sanitizer::Thread,
400-
sanitize_address: *s == Sanitizer::Address,
401-
sanitize_recover: config.sanitizer_recover.contains(s),
397+
Some(llvm::SanitizerOptions {
398+
sanitize_address: config.sanitizer.contains(SanitizerSet::ADDRESS),
399+
sanitize_address_recover: config.sanitizer_recover.contains(SanitizerSet::ADDRESS),
400+
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
401+
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
402402
sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int,
403+
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
403404
})
404405
} else {
405406
None
@@ -600,25 +601,18 @@ pub(crate) unsafe fn optimize(
600601
}
601602

602603
unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static mut llvm::Pass>) {
603-
let sanitizer = match &config.sanitizer {
604-
None => return,
605-
Some(s) => s,
606-
};
607-
608-
let recover = config.sanitizer_recover.contains(sanitizer);
609-
match sanitizer {
610-
Sanitizer::Address => {
611-
passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover));
612-
passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover));
613-
}
614-
Sanitizer::Memory => {
615-
let track_origins = config.sanitizer_memory_track_origins as c_int;
616-
passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover));
617-
}
618-
Sanitizer::Thread => {
619-
passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
620-
}
621-
Sanitizer::Leak => {}
604+
if config.sanitizer.contains(SanitizerSet::ADDRESS) {
605+
let recover = config.sanitizer_recover.contains(SanitizerSet::ADDRESS);
606+
passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover));
607+
passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover));
608+
}
609+
if config.sanitizer.contains(SanitizerSet::MEMORY) {
610+
let track_origins = config.sanitizer_memory_track_origins as c_int;
611+
let recover = config.sanitizer_recover.contains(SanitizerSet::MEMORY);
612+
passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover));
613+
}
614+
if config.sanitizer.contains(SanitizerSet::THREAD) {
615+
passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
622616
}
623617
}
624618

src/librustc_codegen_llvm/base.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ use rustc_codegen_ssa::traits::*;
2929
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
3030
use rustc_data_structures::small_c_str::SmallCStr;
3131
use rustc_middle::dep_graph;
32-
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
32+
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
3333
use rustc_middle::middle::cstore::EncodedMetadata;
3434
use rustc_middle::middle::exported_symbols;
3535
use rustc_middle::mir::mono::{Linkage, Visibility};
3636
use rustc_middle::ty::TyCtxt;
37-
use rustc_session::config::DebugInfo;
37+
use rustc_session::config::{DebugInfo, SanitizerSet};
3838
use rustc_span::symbol::Symbol;
3939

4040
use std::ffi::CString;
@@ -132,7 +132,7 @@ pub fn compile_codegen_unit(
132132
// If this codegen unit contains the main function, also create the
133133
// wrapper here
134134
if let Some(entry) = maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx) {
135-
attributes::sanitize(&cx, CodegenFnAttrFlags::empty(), entry);
135+
attributes::sanitize(&cx, SanitizerSet::empty(), entry);
136136
}
137137

138138
// Run replace-all-uses-with for statics that need it

src/librustc_codegen_llvm/llvm/ffi.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -439,11 +439,12 @@ pub enum OptStage {
439439
/// LLVMRustSanitizerOptions
440440
#[repr(C)]
441441
pub struct SanitizerOptions {
442-
pub sanitize_memory: bool,
443-
pub sanitize_thread: bool,
444442
pub sanitize_address: bool,
445-
pub sanitize_recover: bool,
443+
pub sanitize_address_recover: bool,
444+
pub sanitize_memory: bool,
445+
pub sanitize_memory_recover: bool,
446446
pub sanitize_memory_track_origins: c_int,
447+
pub sanitize_thread: bool,
447448
}
448449

449450
/// LLVMRelocMode

src/librustc_codegen_ssa/back/link.rs

+22-18
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_hir::def_id::CrateNum;
44
use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLib};
55
use rustc_middle::middle::dependency_format::Linkage;
66
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo};
7-
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, Sanitizer};
7+
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SanitizerSet};
88
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
99
use rustc_session::search_paths::PathKind;
1010
use rustc_session::utils::NativeLibKind;
@@ -766,23 +766,26 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
766766
}
767767
}
768768

769-
fn link_sanitizer_runtime(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
770-
let sanitizer = match &sess.opts.debugging_opts.sanitizer {
771-
Some(s) => s,
772-
None => return,
773-
};
774-
769+
fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
775770
if crate_type != CrateType::Executable {
776771
return;
777772
}
773+
let sanitizer = sess.opts.debugging_opts.sanitizer;
774+
if sanitizer.contains(SanitizerSet::ADDRESS) {
775+
link_sanitizer_runtime(sess, linker, "asan");
776+
}
777+
if sanitizer.contains(SanitizerSet::LEAK) {
778+
link_sanitizer_runtime(sess, linker, "lsan");
779+
}
780+
if sanitizer.contains(SanitizerSet::MEMORY) {
781+
link_sanitizer_runtime(sess, linker, "msan");
782+
}
783+
if sanitizer.contains(SanitizerSet::THREAD) {
784+
link_sanitizer_runtime(sess, linker, "tsan");
785+
}
786+
}
778787

779-
let name = match sanitizer {
780-
Sanitizer::Address => "asan",
781-
Sanitizer::Leak => "lsan",
782-
Sanitizer::Memory => "msan",
783-
Sanitizer::Thread => "tsan",
784-
};
785-
788+
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
786789
let default_sysroot = filesearch::get_or_default_sysroot();
787790
let default_tlib =
788791
filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple());
@@ -1555,9 +1558,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
15551558

15561559
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
15571560
if sess.target.target.options.is_like_fuchsia && crate_type == CrateType::Executable {
1558-
let prefix = match sess.opts.debugging_opts.sanitizer {
1559-
Some(Sanitizer::Address) => "asan/",
1560-
_ => "",
1561+
let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
1562+
"asan/"
1563+
} else {
1564+
""
15611565
};
15621566
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
15631567
}
@@ -1581,7 +1585,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
15811585
}
15821586

15831587
// OBJECT-FILES-YES, AUDIT-ORDER
1584-
link_sanitizer_runtime(sess, crate_type, cmd);
1588+
link_sanitizers(sess, crate_type, cmd);
15851589

15861590
// OBJECT-FILES-NO, AUDIT-ORDER
15871591
// Linker plugins should be specified early in the list of arguments

src/librustc_codegen_ssa/back/symbol_export.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_middle::ty::query::Providers;
1515
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
1616
use rustc_middle::ty::Instance;
1717
use rustc_middle::ty::{SymbolName, TyCtxt};
18-
use rustc_session::config::{CrateType, Sanitizer};
18+
use rustc_session::config::{CrateType, SanitizerSet};
1919

2020
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
2121
crates_export_threshold(&tcx.sess.crate_types())
@@ -204,7 +204,7 @@ fn exported_symbols_provider_local(
204204
}));
205205
}
206206

207-
if let Some(Sanitizer::Memory) = tcx.sess.opts.debugging_opts.sanitizer {
207+
if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) {
208208
// Similar to profiling, preserve weak msan symbol during LTO.
209209
const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"];
210210

src/librustc_codegen_ssa/back/write.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rustc_middle::middle::exported_symbols::SymbolExportLevel;
2929
use rustc_middle::ty::TyCtxt;
3030
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
3131
use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType};
32-
use rustc_session::config::{Passes, Sanitizer, SwitchWithOptPath};
32+
use rustc_session::config::{Passes, SanitizerSet, SwitchWithOptPath};
3333
use rustc_session::Session;
3434
use rustc_span::source_map::SourceMap;
3535
use rustc_span::symbol::{sym, Symbol};
@@ -86,8 +86,8 @@ pub struct ModuleConfig {
8686
pub pgo_gen: SwitchWithOptPath,
8787
pub pgo_use: Option<PathBuf>,
8888

89-
pub sanitizer: Option<Sanitizer>,
90-
pub sanitizer_recover: Vec<Sanitizer>,
89+
pub sanitizer: SanitizerSet,
90+
pub sanitizer_recover: SanitizerSet,
9191
pub sanitizer_memory_track_origins: usize,
9292

9393
// Flags indicating which outputs to produce.
@@ -195,10 +195,10 @@ impl ModuleConfig {
195195
),
196196
pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None),
197197

198-
sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer.clone(), None),
198+
sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()),
199199
sanitizer_recover: if_regular!(
200-
sess.opts.debugging_opts.sanitizer_recover.clone(),
201-
vec![]
200+
sess.opts.debugging_opts.sanitizer_recover,
201+
SanitizerSet::empty()
202202
),
203203
sanitizer_memory_track_origins: if_regular!(
204204
sess.opts.debugging_opts.sanitizer_memory_track_origins,

src/librustc_interface/tests.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use rustc_session::config::Strip;
66
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
77
use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes};
88
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
9-
use rustc_session::config::{Externs, OutputType, OutputTypes, Sanitizer, SymbolManglingVersion};
9+
use rustc_session::config::{
10+
Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion,
11+
};
1012
use rustc_session::lint::Level;
1113
use rustc_session::search_paths::SearchPath;
1214
use rustc_session::utils::NativeLibKind;
@@ -569,9 +571,9 @@ fn test_debugging_options_tracking_hash() {
569571
tracked!(relro_level, Some(RelroLevel::Full));
570572
tracked!(report_delayed_bugs, true);
571573
tracked!(run_dsymutil, false);
572-
tracked!(sanitizer, Some(Sanitizer::Address));
574+
tracked!(sanitizer, SanitizerSet::ADDRESS);
573575
tracked!(sanitizer_memory_track_origins, 2);
574-
tracked!(sanitizer_recover, vec![Sanitizer::Address]);
576+
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
575577
tracked!(saturating_float_casts, Some(true));
576578
tracked!(share_generics, Some(true));
577579
tracked!(show_span, Some(String::from("abc")));

src/librustc_middle/middle/codegen_fn_attrs.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::mir::mono::Linkage;
22
use rustc_attr::{InlineAttr, OptimizeAttr};
3+
use rustc_session::config::SanitizerSet;
34
use rustc_span::symbol::Symbol;
45

56
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
@@ -30,6 +31,9 @@ pub struct CodegenFnAttrs {
3031
/// The `#[link_section = "..."]` attribute, or what executable section this
3132
/// should be placed in.
3233
pub link_section: Option<Symbol>,
34+
/// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which
35+
/// instrumentation should be disabled inside the annotated function.
36+
pub no_sanitize: SanitizerSet,
3337
}
3438

3539
bitflags! {
@@ -69,20 +73,12 @@ bitflags! {
6973
const FFI_RETURNS_TWICE = 1 << 10;
7074
/// `#[track_caller]`: allow access to the caller location
7175
const TRACK_CALLER = 1 << 11;
72-
/// `#[no_sanitize(address)]`: disables address sanitizer instrumentation
73-
const NO_SANITIZE_ADDRESS = 1 << 12;
74-
/// `#[no_sanitize(memory)]`: disables memory sanitizer instrumentation
75-
const NO_SANITIZE_MEMORY = 1 << 13;
76-
/// `#[no_sanitize(thread)]`: disables thread sanitizer instrumentation
77-
const NO_SANITIZE_THREAD = 1 << 14;
78-
/// All `#[no_sanitize(...)]` attributes.
79-
const NO_SANITIZE_ANY = Self::NO_SANITIZE_ADDRESS.bits | Self::NO_SANITIZE_MEMORY.bits | Self::NO_SANITIZE_THREAD.bits;
8076
/// #[ffi_pure]: applies clang's `pure` attribute to a foreign function
8177
/// declaration.
82-
const FFI_PURE = 1 << 15;
78+
const FFI_PURE = 1 << 12;
8379
/// #[ffi_const]: applies clang's `const` attribute to a foreign function
8480
/// declaration.
85-
const FFI_CONST = 1 << 16;
81+
const FFI_CONST = 1 << 13;
8682
}
8783
}
8884

@@ -98,6 +94,7 @@ impl CodegenFnAttrs {
9894
target_features: vec![],
9995
linkage: None,
10096
link_section: None,
97+
no_sanitize: SanitizerSet::empty(),
10198
}
10299
}
103100

0 commit comments

Comments
 (0)