Skip to content

Commit 2e1551d

Browse files
committed
Auto merge of #66522 - tmiasko:sanitize-flags, r=alexcrichton
Add support for sanitizer recover and tracking origins of uninitialized memory * Add support for sanitizer recovery `-Zsanitizer-recover=...` (equivalent to `-fsanitize-recover` in clang). * Add support for tracking origins of uninitialized memory in MemorySanitizer `-Zsanitizer-memory-track-origins` (equivalent to `-fsanitize-memory-track-origins` in clang).
2 parents 083b5a0 + 3b45626 commit 2e1551d

File tree

7 files changed

+211
-32
lines changed

7 files changed

+211
-32
lines changed

src/librustc/session/config.rs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,27 @@ pub struct Config {
4343
pub usize_ty: UintTy,
4444
}
4545

46-
#[derive(Clone, Hash, Debug)]
46+
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
4747
pub enum Sanitizer {
4848
Address,
4949
Leak,
5050
Memory,
5151
Thread,
5252
}
5353

54+
impl FromStr for Sanitizer {
55+
type Err = ();
56+
fn from_str(s: &str) -> Result<Sanitizer, ()> {
57+
match s {
58+
"address" => Ok(Sanitizer::Address),
59+
"leak" => Ok(Sanitizer::Leak),
60+
"memory" => Ok(Sanitizer::Memory),
61+
"thread" => Ok(Sanitizer::Thread),
62+
_ => Err(()),
63+
}
64+
}
65+
}
66+
5467
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
5568
pub enum OptLevel {
5669
No, // -O0
@@ -819,6 +832,9 @@ macro_rules! options {
819832
Some("one of: `full`, `partial`, or `off`");
820833
pub const parse_sanitizer: Option<&str> =
821834
Some("one of: `address`, `leak`, `memory` or `thread`");
835+
pub const parse_sanitizer_list: Option<&str> =
836+
Some("comma separated list of sanitizers");
837+
pub const parse_sanitizer_memory_track_origins: Option<&str> = None;
822838
pub const parse_linker_flavor: Option<&str> =
823839
Some(::rustc_target::spec::LinkerFlavor::one_of());
824840
pub const parse_optimization_fuel: Option<&str> =
@@ -1013,15 +1029,46 @@ macro_rules! options {
10131029
true
10141030
}
10151031

1016-
fn parse_sanitizer(slote: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
1017-
match v {
1018-
Some("address") => *slote = Some(Sanitizer::Address),
1019-
Some("leak") => *slote = Some(Sanitizer::Leak),
1020-
Some("memory") => *slote = Some(Sanitizer::Memory),
1021-
Some("thread") => *slote = Some(Sanitizer::Thread),
1022-
_ => return false,
1032+
fn parse_sanitizer(slot: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
1033+
if let Some(Ok(s)) = v.map(str::parse) {
1034+
*slot = Some(s);
1035+
true
1036+
} else {
1037+
false
1038+
}
1039+
}
1040+
1041+
fn parse_sanitizer_list(slot: &mut Vec<Sanitizer>, v: Option<&str>) -> bool {
1042+
if let Some(v) = v {
1043+
for s in v.split(',').map(str::parse) {
1044+
if let Ok(s) = s {
1045+
if !slot.contains(&s) {
1046+
slot.push(s);
1047+
}
1048+
} else {
1049+
return false;
1050+
}
1051+
}
1052+
true
1053+
} else {
1054+
false
1055+
}
1056+
}
1057+
1058+
fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool {
1059+
match v.map(|s| s.parse()) {
1060+
None => {
1061+
*slot = 2;
1062+
true
1063+
}
1064+
Some(Ok(i)) if i <= 2 => {
1065+
*slot = i;
1066+
true
1067+
}
1068+
_ => {
1069+
false
1070+
}
10231071
}
1024-
true
10251072
}
10261073

10271074
fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
@@ -1379,6 +1426,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
13791426
"pass `-install_name @rpath/...` to the macOS linker"),
13801427
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
13811428
"use a sanitizer"),
1429+
sanitizer_recover: Vec<Sanitizer> = (vec![], parse_sanitizer_list, [TRACKED],
1430+
"Enable recovery for selected sanitizers"),
1431+
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
1432+
"Enable origins tracking in MemorySanitizer"),
13821433
fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
13831434
"set the optimization fuel quota for a crate"),
13841435
print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
@@ -2984,6 +3035,7 @@ mod dep_tracking {
29843035
Option<cstore::NativeLibraryKind>
29853036
));
29863037
impl_dep_tracking_hash_for_sortable_vec_of!((String, u64));
3038+
impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer);
29873039

29883040
impl<T1, T2> DepTrackingHash for (T1, T2)
29893041
where

src/librustc_codegen_llvm/back/write.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::LlvmCodegenBackend;
1313
use rustc::hir::def_id::LOCAL_CRATE;
1414
use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig, run_assembler};
1515
use rustc_codegen_ssa::traits::*;
16-
use rustc::session::config::{self, OutputType, Passes, Lto, SwitchWithOptPath};
16+
use rustc::session::config::{self, OutputType, Passes, Lto, Sanitizer, SwitchWithOptPath};
1717
use rustc::session::Session;
1818
use rustc::ty::TyCtxt;
1919
use rustc_codegen_ssa::{RLIB_BYTECODE_EXTENSION, ModuleCodegen, CompiledModule};
@@ -29,7 +29,7 @@ use std::path::{Path, PathBuf};
2929
use std::str;
3030
use std::sync::Arc;
3131
use std::slice;
32-
use libc::{c_uint, c_void, c_char, size_t};
32+
use libc::{c_int, c_uint, c_void, c_char, size_t};
3333

3434
pub const RELOC_MODEL_ARGS : [(&str, llvm::RelocMode); 7] = [
3535
("pic", llvm::RelocMode::PIC),
@@ -323,7 +323,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
323323
llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
324324
}
325325

326-
if config.opt_level.is_some() {
326+
if let Some(opt_level) = config.opt_level {
327327
// Create the two optimizing pass managers. These mirror what clang
328328
// does, and are by populated by LLVM's default PassManagerBuilder.
329329
// Each manager has a different set of passes, but they also share
@@ -363,6 +363,8 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
363363
}
364364
}
365365

366+
add_sanitizer_passes(config, &mut extra_passes);
367+
366368
for pass_name in &cgcx.plugin_passes {
367369
if let Some(pass) = find_pass(pass_name) {
368370
extra_passes.push(pass);
@@ -384,8 +386,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
384386
if !config.no_prepopulate_passes {
385387
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
386388
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
387-
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
388-
.unwrap_or(llvm::CodeGenOptLevel::None);
389+
let opt_level = to_llvm_opt_settings(opt_level).0;
389390
let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
390391
(cgcx.lto != Lto::Fat && cgcx.opts.cg.linker_plugin_lto.enabled());
391392
with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| {
@@ -449,6 +450,31 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
449450
Ok(())
450451
}
451452

453+
unsafe fn add_sanitizer_passes(config: &ModuleConfig,
454+
passes: &mut Vec<&'static mut llvm::Pass>) {
455+
456+
let sanitizer = match &config.sanitizer {
457+
None => return,
458+
Some(s) => s,
459+
};
460+
461+
let recover = config.sanitizer_recover.contains(sanitizer);
462+
match sanitizer {
463+
Sanitizer::Address => {
464+
passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover));
465+
passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover));
466+
}
467+
Sanitizer::Memory => {
468+
let track_origins = config.sanitizer_memory_track_origins as c_int;
469+
passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover));
470+
}
471+
Sanitizer::Thread => {
472+
passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
473+
}
474+
Sanitizer::Leak => {}
475+
}
476+
}
477+
452478
pub(crate) unsafe fn codegen(cgcx: &CodegenContext<LlvmCodegenBackend>,
453479
diag_handler: &Handler,
454480
module: ModuleCodegen<ModuleLlvm>,

src/librustc_codegen_llvm/llvm/ffi.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,11 @@ extern "C" {
16701670

16711671
pub fn LLVMRustPassKind(Pass: &Pass) -> PassKind;
16721672
pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> Option<&'static mut Pass>;
1673+
pub fn LLVMRustCreateAddressSanitizerFunctionPass(Recover: bool) -> &'static mut Pass;
1674+
pub fn LLVMRustCreateModuleAddressSanitizerPass(Recover: bool) -> &'static mut Pass;
1675+
pub fn LLVMRustCreateMemorySanitizerPass(TrackOrigins: c_int,
1676+
Recover: bool) -> &'static mut Pass;
1677+
pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass;
16731678
pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass);
16741679
pub fn LLVMRustAddLastExtensionPasses(PMB: &PassManagerBuilder,
16751680
Passes: *const &'static mut Pass,

src/librustc_codegen_ssa/back/write.rs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ pub struct ModuleConfig {
5959
pub pgo_gen: SwitchWithOptPath,
6060
pub pgo_use: Option<PathBuf>,
6161

62+
pub sanitizer: Option<Sanitizer>,
63+
pub sanitizer_recover: Vec<Sanitizer>,
64+
pub sanitizer_memory_track_origins: usize,
65+
6266
// Flags indicating which outputs to produce.
6367
pub emit_pre_lto_bc: bool,
6468
pub emit_no_opt_bc: bool,
@@ -97,6 +101,10 @@ impl ModuleConfig {
97101
pgo_gen: SwitchWithOptPath::Disabled,
98102
pgo_use: None,
99103

104+
sanitizer: None,
105+
sanitizer_recover: Default::default(),
106+
sanitizer_memory_track_origins: 0,
107+
100108
emit_no_opt_bc: false,
101109
emit_pre_lto_bc: false,
102110
emit_bc: false,
@@ -345,29 +353,16 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
345353
let mut metadata_config = ModuleConfig::new(vec![]);
346354
let mut allocator_config = ModuleConfig::new(vec![]);
347355

348-
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
349-
match *sanitizer {
350-
Sanitizer::Address => {
351-
modules_config.passes.push("asan".to_owned());
352-
modules_config.passes.push("asan-module".to_owned());
353-
}
354-
Sanitizer::Memory => {
355-
modules_config.passes.push("msan".to_owned())
356-
}
357-
Sanitizer::Thread => {
358-
modules_config.passes.push("tsan".to_owned())
359-
}
360-
_ => {}
361-
}
362-
}
363-
364356
if sess.opts.debugging_opts.profile {
365357
modules_config.passes.push("insert-gcov-profiling".to_owned())
366358
}
367359

368360
modules_config.pgo_gen = sess.opts.cg.profile_generate.clone();
369361
modules_config.pgo_use = sess.opts.cg.profile_use.clone();
370-
362+
modules_config.sanitizer = sess.opts.debugging_opts.sanitizer.clone();
363+
modules_config.sanitizer_recover = sess.opts.debugging_opts.sanitizer_recover.clone();
364+
modules_config.sanitizer_memory_track_origins =
365+
sess.opts.debugging_opts.sanitizer_memory_track_origins;
371366
modules_config.opt_level = Some(sess.opts.optimize);
372367
modules_config.opt_size = Some(sess.opts.optimize);
373368

src/rustllvm/PassWrapper.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,17 @@
2020
#include "llvm/Transforms/IPO/FunctionImport.h"
2121
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
2222
#include "llvm/LTO/LTO.h"
23-
2423
#include "llvm-c/Transforms/PassManagerBuilder.h"
2524

25+
#include "llvm/Transforms/Instrumentation.h"
26+
#if LLVM_VERSION_GE(9, 0)
27+
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
28+
#endif
29+
#if LLVM_VERSION_GE(8, 0)
30+
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
31+
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
32+
#endif
33+
2634
using namespace llvm;
2735
using namespace llvm::legacy;
2836

@@ -76,6 +84,41 @@ extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
7684
return nullptr;
7785
}
7886

87+
extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) {
88+
const bool CompileKernel = false;
89+
90+
return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover));
91+
}
92+
93+
extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) {
94+
const bool CompileKernel = false;
95+
96+
#if LLVM_VERSION_GE(9, 0)
97+
return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover));
98+
#else
99+
return wrap(createAddressSanitizerModulePass(CompileKernel, Recover));
100+
#endif
101+
}
102+
103+
extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) {
104+
#if LLVM_VERSION_GE(8, 0)
105+
const bool CompileKernel = false;
106+
107+
return wrap(createMemorySanitizerLegacyPassPass(
108+
MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel}));
109+
#else
110+
return wrap(createMemorySanitizerPass(TrackOrigins, Recover));
111+
#endif
112+
}
113+
114+
extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
115+
#if LLVM_VERSION_GE(8, 0)
116+
return wrap(createThreadSanitizerLegacyPassPass());
117+
#else
118+
return wrap(createThreadSanitizerPass());
119+
#endif
120+
}
121+
79122
extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
80123
assert(RustPass);
81124
Pass *Pass = unwrap(RustPass);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Verifies that MemorySanitizer track-origins level can be controlled
2+
// with -Zsanitizer-memory-track-origins option.
3+
//
4+
// needs-sanitizer-support
5+
// revisions:MSAN-0 MSAN-1 MSAN-2
6+
//
7+
//[MSAN-0] compile-flags: -Zsanitizer=memory
8+
//[MSAN-1] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins=1
9+
//[MSAN-2] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins
10+
11+
#![crate_type="lib"]
12+
13+
// MSAN-0-NOT: @__msan_track_origins
14+
// MSAN-1: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 1
15+
// MSAN-2: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 2
16+
//
17+
// MSAN-0-LABEL: define void @copy(
18+
// MSAN-1-LABEL: define void @copy(
19+
// MSAN-2-LABEL: define void @copy(
20+
#[no_mangle]
21+
pub fn copy(dst: &mut i32, src: &i32) {
22+
// MSAN-0-NOT: call i32 @__msan_chain_origin(
23+
// MSAN-1-NOT: call i32 @__msan_chain_origin(
24+
// MSAN-2: call i32 @__msan_chain_origin(
25+
*dst = *src;
26+
}

src/test/codegen/sanitizer-recover.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Verifies that AddressSanitizer and MemorySanitizer
2+
// recovery mode can be enabled with -Zsanitizer-recover.
3+
//
4+
// needs-sanitizer-support
5+
// revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER
6+
//
7+
//[ASAN] compile-flags: -Zsanitizer=address
8+
//[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address
9+
//[MSAN] compile-flags: -Zsanitizer=memory
10+
//[MSAN-RECOVER] compile-flags: -Zsanitizer=memory -Zsanitizer-recover=memory
11+
12+
#![crate_type="lib"]
13+
14+
// ASAN-LABEL: define i32 @penguin(
15+
// ASAN-RECOVER-LABEL: define i32 @penguin(
16+
// MSAN-LABEL: define i32 @penguin(
17+
// MSAN-RECOVER-LABEL: define i32 @penguin(
18+
#[no_mangle]
19+
pub fn penguin(p: &mut i32) -> i32 {
20+
// ASAN: call void @__asan_report_load4(i64 %0)
21+
// ASAN: unreachable
22+
//
23+
// ASAN-RECOVER: call void @__asan_report_load4_noabort(
24+
// ASAN-RECOVER-NOT: unreachable
25+
//
26+
// MSAN: call void @__msan_warning_noreturn()
27+
// MSAN: unreachable
28+
//
29+
// MSAN-RECOVER: call void @__msan_warning()
30+
// MSAN-RECOVER-NOT: unreachable
31+
*p
32+
}

0 commit comments

Comments
 (0)