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).
This commit is contained in:
bors 2019-11-26 01:54:53 +00:00
commit 2626f3d3d5
7 changed files with 215 additions and 32 deletions

View File

@ -40,7 +40,7 @@ pub struct Config {
pub usize_ty: UintTy,
}
#[derive(Clone, Hash, Debug)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Sanitizer {
Address,
Leak,
@ -48,6 +48,19 @@ pub enum Sanitizer {
Thread,
}
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(()),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum OptLevel {
No, // -O0
@ -816,6 +829,9 @@ macro_rules! options {
Some("one of: `full`, `partial`, or `off`");
pub const parse_sanitizer: Option<&str> =
Some("one of: `address`, `leak`, `memory` or `thread`");
pub const parse_sanitizer_list: Option<&str> =
Some("comma separated list of sanitizers");
pub const parse_sanitizer_memory_track_origins: Option<&str> = None;
pub const parse_linker_flavor: Option<&str> =
Some(::rustc_target::spec::LinkerFlavor::one_of());
pub const parse_optimization_fuel: Option<&str> =
@ -1010,15 +1026,46 @@ macro_rules! options {
true
}
fn parse_sanitizer(slote: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
match v {
Some("address") => *slote = Some(Sanitizer::Address),
Some("leak") => *slote = Some(Sanitizer::Leak),
Some("memory") => *slote = Some(Sanitizer::Memory),
Some("thread") => *slote = Some(Sanitizer::Thread),
_ => return false,
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 {
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;
}
}
true
} else {
false
}
}
fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool {
match v.map(|s| s.parse()) {
None => {
*slot = 2;
true
}
Some(Ok(i)) if i <= 2 => {
*slot = i;
true
}
_ => {
false
}
}
true
}
fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
@ -1376,6 +1423,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"pass `-install_name @rpath/...` to the macOS linker"),
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
"use a sanitizer"),
sanitizer_recover: Vec<Sanitizer> = (vec![], parse_sanitizer_list, [TRACKED],
"Enable recovery for selected sanitizers"),
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
"Enable origins tracking in MemorySanitizer"),
fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
"set the optimization fuel quota for a crate"),
print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
@ -2881,6 +2932,7 @@ mod dep_tracking {
Option<cstore::NativeLibraryKind>
));
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

View File

@ -13,7 +13,7 @@ use crate::LlvmCodegenBackend;
use rustc::hir::def_id::LOCAL_CRATE;
use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig, run_assembler};
use rustc_codegen_ssa::traits::*;
use rustc::session::config::{self, OutputType, Passes, Lto, SwitchWithOptPath};
use rustc::session::config::{self, OutputType, Passes, Lto, Sanitizer, SwitchWithOptPath};
use rustc::session::Session;
use rustc::ty::TyCtxt;
use rustc_codegen_ssa::{RLIB_BYTECODE_EXTENSION, ModuleCodegen, CompiledModule};
@ -29,7 +29,7 @@ use std::path::{Path, PathBuf};
use std::str;
use std::sync::Arc;
use std::slice;
use libc::{c_uint, c_void, c_char, size_t};
use libc::{c_int, c_uint, c_void, c_char, size_t};
pub const RELOC_MODEL_ARGS : [(&str, llvm::RelocMode); 7] = [
("pic", llvm::RelocMode::PIC),
@ -323,7 +323,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
}
if config.opt_level.is_some() {
if let Some(opt_level) = config.opt_level {
// Create the two optimizing pass managers. These mirror what clang
// does, and are by populated by LLVM's default PassManagerBuilder.
// Each manager has a different set of passes, but they also share
@ -363,6 +363,8 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
}
}
add_sanitizer_passes(config, &mut extra_passes);
for pass_name in &cgcx.plugin_passes {
if let Some(pass) = find_pass(pass_name) {
extra_passes.push(pass);
@ -384,8 +386,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
if !config.no_prepopulate_passes {
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
.unwrap_or(llvm::CodeGenOptLevel::None);
let opt_level = to_llvm_opt_settings(opt_level).0;
let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
(cgcx.lto != Lto::Fat && cgcx.opts.cg.linker_plugin_lto.enabled());
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>,
Ok(())
}
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 => {}
}
}
pub(crate) unsafe fn codegen(cgcx: &CodegenContext<LlvmCodegenBackend>,
diag_handler: &Handler,
module: ModuleCodegen<ModuleLlvm>,

View File

@ -1670,6 +1670,11 @@ extern "C" {
pub fn LLVMRustPassKind(Pass: &Pass) -> PassKind;
pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> Option<&'static mut Pass>;
pub fn LLVMRustCreateAddressSanitizerFunctionPass(Recover: bool) -> &'static mut Pass;
pub fn LLVMRustCreateModuleAddressSanitizerPass(Recover: bool) -> &'static mut Pass;
pub fn LLVMRustCreateMemorySanitizerPass(TrackOrigins: c_int,
Recover: bool) -> &'static mut Pass;
pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass;
pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass);
pub fn LLVMRustAddLastExtensionPasses(PMB: &PassManagerBuilder,
Passes: *const &'static mut Pass,

View File

@ -59,6 +59,10 @@ pub struct ModuleConfig {
pub pgo_gen: SwitchWithOptPath,
pub pgo_use: Option<PathBuf>,
pub sanitizer: Option<Sanitizer>,
pub sanitizer_recover: Vec<Sanitizer>,
pub sanitizer_memory_track_origins: usize,
// Flags indicating which outputs to produce.
pub emit_pre_lto_bc: bool,
pub emit_no_opt_bc: bool,
@ -97,6 +101,10 @@ impl ModuleConfig {
pgo_gen: SwitchWithOptPath::Disabled,
pgo_use: None,
sanitizer: None,
sanitizer_recover: Default::default(),
sanitizer_memory_track_origins: 0,
emit_no_opt_bc: false,
emit_pre_lto_bc: false,
emit_bc: false,
@ -345,29 +353,16 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
let mut metadata_config = ModuleConfig::new(vec![]);
let mut allocator_config = ModuleConfig::new(vec![]);
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
match *sanitizer {
Sanitizer::Address => {
modules_config.passes.push("asan".to_owned());
modules_config.passes.push("asan-module".to_owned());
}
Sanitizer::Memory => {
modules_config.passes.push("msan".to_owned())
}
Sanitizer::Thread => {
modules_config.passes.push("tsan".to_owned())
}
_ => {}
}
}
if sess.opts.debugging_opts.profile {
modules_config.passes.push("insert-gcov-profiling".to_owned())
}
modules_config.pgo_gen = sess.opts.cg.profile_generate.clone();
modules_config.pgo_use = sess.opts.cg.profile_use.clone();
modules_config.sanitizer = sess.opts.debugging_opts.sanitizer.clone();
modules_config.sanitizer_recover = sess.opts.debugging_opts.sanitizer_recover.clone();
modules_config.sanitizer_memory_track_origins =
sess.opts.debugging_opts.sanitizer_memory_track_origins;
modules_config.opt_level = Some(sess.opts.optimize);
modules_config.opt_size = Some(sess.opts.optimize);

View File

@ -20,9 +20,17 @@
#include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#include "llvm/LTO/LTO.h"
#include "llvm-c/Transforms/PassManagerBuilder.h"
#include "llvm/Transforms/Instrumentation.h"
#if LLVM_VERSION_GE(9, 0)
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
#endif
#if LLVM_VERSION_GE(8, 0)
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
#endif
using namespace llvm;
using namespace llvm::legacy;
@ -76,6 +84,41 @@ extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
return nullptr;
}
extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) {
const bool CompileKernel = false;
return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover));
}
extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) {
const bool CompileKernel = false;
#if LLVM_VERSION_GE(9, 0)
return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover));
#else
return wrap(createAddressSanitizerModulePass(CompileKernel, Recover));
#endif
}
extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) {
#if LLVM_VERSION_GE(8, 0)
const bool CompileKernel = false;
return wrap(createMemorySanitizerLegacyPassPass(
MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel}));
#else
return wrap(createMemorySanitizerPass(TrackOrigins, Recover));
#endif
}
extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
#if LLVM_VERSION_GE(8, 0)
return wrap(createThreadSanitizerLegacyPassPass());
#else
return wrap(createThreadSanitizerPass());
#endif
}
extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
assert(RustPass);
Pass *Pass = unwrap(RustPass);

View File

@ -0,0 +1,28 @@
// Verifies that MemorySanitizer track-origins level can be controlled
// with -Zsanitizer-memory-track-origins option.
//
// needs-sanitizer-support
// only-linux
// only-x86_64
// revisions:MSAN-0 MSAN-1 MSAN-2
//
//[MSAN-0] compile-flags: -Zsanitizer=memory
//[MSAN-1] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins=1
//[MSAN-2] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins
#![crate_type="lib"]
// MSAN-0-NOT: @__msan_track_origins
// MSAN-1: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 1
// MSAN-2: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 2
//
// MSAN-0-LABEL: define void @copy(
// MSAN-1-LABEL: define void @copy(
// MSAN-2-LABEL: define void @copy(
#[no_mangle]
pub fn copy(dst: &mut i32, src: &i32) {
// MSAN-0-NOT: call i32 @__msan_chain_origin(
// MSAN-1-NOT: call i32 @__msan_chain_origin(
// MSAN-2: call i32 @__msan_chain_origin(
*dst = *src;
}

View File

@ -0,0 +1,34 @@
// Verifies that AddressSanitizer and MemorySanitizer
// recovery mode can be enabled with -Zsanitizer-recover.
//
// needs-sanitizer-support
// only-linux
// only-x86_64
// revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER
//
//[ASAN] compile-flags: -Zsanitizer=address
//[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address
//[MSAN] compile-flags: -Zsanitizer=memory
//[MSAN-RECOVER] compile-flags: -Zsanitizer=memory -Zsanitizer-recover=memory
#![crate_type="lib"]
// ASAN-LABEL: define i32 @penguin(
// ASAN-RECOVER-LABEL: define i32 @penguin(
// MSAN-LABEL: define i32 @penguin(
// MSAN-RECOVER-LABEL: define i32 @penguin(
#[no_mangle]
pub fn penguin(p: &mut i32) -> i32 {
// ASAN: call void @__asan_report_load4(i64 %0)
// ASAN: unreachable
//
// ASAN-RECOVER: call void @__asan_report_load4_noabort(
// ASAN-RECOVER-NOT: unreachable
//
// MSAN: call void @__msan_warning_noreturn()
// MSAN: unreachable
//
// MSAN-RECOVER: call void @__msan_warning()
// MSAN-RECOVER-NOT: unreachable
*p
}