Skip to content

Commit 2626f3d

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 483a83b + bf121a3 commit 2626f3d

File tree

7 files changed

+215
-32
lines changed

7 files changed

+215
-32
lines changed

src/librustc/session/config.rs

+61-9
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,27 @@ pub struct Config {
4040
pub usize_ty: UintTy,
4141
}
4242

43-
#[derive(Clone, Hash, Debug)]
43+
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
4444
pub enum Sanitizer {
4545
Address,
4646
Leak,
4747
Memory,
4848
Thread,
4949
}
5050

51+
impl FromStr for Sanitizer {
52+
type Err = ();
53+
fn from_str(s: &str) -> Result<Sanitizer, ()> {
54+
match s {
55+
"address" => Ok(Sanitizer::Address),
56+
"leak" => Ok(Sanitizer::Leak),
57+
"memory" => Ok(Sanitizer::Memory),
58+
"thread" => Ok(Sanitizer::Thread),
59+
_ => Err(()),
60+
}
61+
}
62+
}
63+
5164
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
5265
pub enum OptLevel {
5366
No, // -O0
@@ -816,6 +829,9 @@ macro_rules! options {
816829
Some("one of: `full`, `partial`, or `off`");
817830
pub const parse_sanitizer: Option<&str> =
818831
Some("one of: `address`, `leak`, `memory` or `thread`");
832+
pub const parse_sanitizer_list: Option<&str> =
833+
Some("comma separated list of sanitizers");
834+
pub const parse_sanitizer_memory_track_origins: Option<&str> = None;
819835
pub const parse_linker_flavor: Option<&str> =
820836
Some(::rustc_target::spec::LinkerFlavor::one_of());
821837
pub const parse_optimization_fuel: Option<&str> =
@@ -1010,15 +1026,46 @@ macro_rules! options {
10101026
true
10111027
}
10121028

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

10241071
fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
@@ -1376,6 +1423,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
13761423
"pass `-install_name @rpath/...` to the macOS linker"),
13771424
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
13781425
"use a sanitizer"),
1426+
sanitizer_recover: Vec<Sanitizer> = (vec![], parse_sanitizer_list, [TRACKED],
1427+
"Enable recovery for selected sanitizers"),
1428+
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
1429+
"Enable origins tracking in MemorySanitizer"),
13791430
fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
13801431
"set the optimization fuel quota for a crate"),
13811432
print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
@@ -2881,6 +2932,7 @@ mod dep_tracking {
28812932
Option<cstore::NativeLibraryKind>
28822933
));
28832934
impl_dep_tracking_hash_for_sortable_vec_of!((String, u64));
2935+
impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer);
28842936

28852937
impl<T1, T2> DepTrackingHash for (T1, T2)
28862938
where

src/librustc_codegen_llvm/back/write.rs

+31-5
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

+5
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

+12-17
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

+44-1
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);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Verifies that MemorySanitizer track-origins level can be controlled
2+
// with -Zsanitizer-memory-track-origins option.
3+
//
4+
// needs-sanitizer-support
5+
// only-linux
6+
// only-x86_64
7+
// revisions:MSAN-0 MSAN-1 MSAN-2
8+
//
9+
//[MSAN-0] compile-flags: -Zsanitizer=memory
10+
//[MSAN-1] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins=1
11+
//[MSAN-2] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins
12+
13+
#![crate_type="lib"]
14+
15+
// MSAN-0-NOT: @__msan_track_origins
16+
// MSAN-1: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 1
17+
// MSAN-2: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 2
18+
//
19+
// MSAN-0-LABEL: define void @copy(
20+
// MSAN-1-LABEL: define void @copy(
21+
// MSAN-2-LABEL: define void @copy(
22+
#[no_mangle]
23+
pub fn copy(dst: &mut i32, src: &i32) {
24+
// MSAN-0-NOT: call i32 @__msan_chain_origin(
25+
// MSAN-1-NOT: call i32 @__msan_chain_origin(
26+
// MSAN-2: call i32 @__msan_chain_origin(
27+
*dst = *src;
28+
}

src/test/codegen/sanitizer-recover.rs

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

0 commit comments

Comments
 (0)