mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-30 10:45:18 +00:00
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:
commit
2626f3d3d5
@ -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
|
||||
|
@ -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>,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
28
src/test/codegen/sanitizer-memory-track-orgins.rs
Normal file
28
src/test/codegen/sanitizer-memory-track-orgins.rs
Normal 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;
|
||||
}
|
34
src/test/codegen/sanitizer-recover.rs
Normal file
34
src/test/codegen/sanitizer-recover.rs
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user