1
Fork 0

HWASan support

This commit is contained in:
Tri Vo 2021-01-22 18:32:38 -08:00
parent 0b7a598e12
commit c7d9bffe76
18 changed files with 100 additions and 12 deletions

View file

@ -53,6 +53,9 @@ pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll V
if enabled.contains(SanitizerSet::THREAD) { if enabled.contains(SanitizerSet::THREAD) {
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
} }
if enabled.contains(SanitizerSet::HWADDRESS) {
llvm::Attribute::SanitizeHWAddress.apply_llfn(Function, llfn);
}
} }
/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.

View file

@ -440,6 +440,8 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY), sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int, sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int,
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS),
}) })
} else { } else {
None None
@ -652,6 +654,10 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static
if config.sanitizer.contains(SanitizerSet::THREAD) { if config.sanitizer.contains(SanitizerSet::THREAD) {
passes.push(llvm::LLVMRustCreateThreadSanitizerPass()); passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
} }
if config.sanitizer.contains(SanitizerSet::HWADDRESS) {
let recover = config.sanitizer_recover.contains(SanitizerSet::HWADDRESS);
passes.push(llvm::LLVMRustCreateHWAddressSanitizerPass(recover));
}
} }
pub(crate) fn link( pub(crate) fn link(

View file

@ -131,6 +131,7 @@ pub enum Attribute {
ReturnsTwice = 25, ReturnsTwice = 25,
ReadNone = 26, ReadNone = 26,
InaccessibleMemOnly = 27, InaccessibleMemOnly = 27,
SanitizeHWAddress = 28,
} }
/// LLVMIntPredicate /// LLVMIntPredicate
@ -439,6 +440,8 @@ pub struct SanitizerOptions {
pub sanitize_memory_recover: bool, pub sanitize_memory_recover: bool,
pub sanitize_memory_track_origins: c_int, pub sanitize_memory_track_origins: c_int,
pub sanitize_thread: bool, pub sanitize_thread: bool,
pub sanitize_hwaddress: bool,
pub sanitize_hwaddress_recover: bool,
} }
/// LLVMRelocMode /// LLVMRelocMode
@ -2128,6 +2131,7 @@ extern "C" {
Recover: bool, Recover: bool,
) -> &'static mut Pass; ) -> &'static mut Pass;
pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass; pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass;
pub fn LLVMRustCreateHWAddressSanitizerPass(Recover: bool) -> &'static mut Pass;
pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass); pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass);
pub fn LLVMRustAddLastExtensionPasses( pub fn LLVMRustAddLastExtensionPasses(
PMB: &PassManagerBuilder, PMB: &PassManagerBuilder,

View file

@ -893,6 +893,9 @@ fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linke
if sanitizer.contains(SanitizerSet::THREAD) { if sanitizer.contains(SanitizerSet::THREAD) {
link_sanitizer_runtime(sess, linker, "tsan"); link_sanitizer_runtime(sess, linker, "tsan");
} }
if sanitizer.contains(SanitizerSet::HWADDRESS) {
link_sanitizer_runtime(sess, linker, "hwasan");
}
} }
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {

View file

@ -85,6 +85,7 @@ enum LLVMRustAttribute {
ReturnsTwice = 25, ReturnsTwice = 25,
ReadNone = 26, ReadNone = 26,
InaccessibleMemOnly = 27, InaccessibleMemOnly = 27,
SanitizeHWAddress = 28,
}; };
typedef struct OpaqueRustString *RustStringRef; typedef struct OpaqueRustString *RustStringRef;

View file

@ -33,6 +33,7 @@
#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/TimeProfiler.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
#include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h"
#include "llvm/Transforms/Utils/NameAnonGlobals.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h"
@ -133,6 +134,12 @@ extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
return wrap(createThreadSanitizerLegacyPassPass()); return wrap(createThreadSanitizerLegacyPassPass());
} }
extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) {
const bool CompileKernel = false;
return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover));
}
extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
assert(RustPass); assert(RustPass);
Pass *Pass = unwrap(RustPass); Pass *Pass = unwrap(RustPass);
@ -722,6 +729,8 @@ struct LLVMRustSanitizerOptions {
bool SanitizeMemoryRecover; bool SanitizeMemoryRecover;
int SanitizeMemoryTrackOrigins; int SanitizeMemoryTrackOrigins;
bool SanitizeThread; bool SanitizeThread;
bool SanitizeHWAddress;
bool SanitizeHWAddressRecover;
}; };
extern "C" void extern "C" void
@ -886,6 +895,23 @@ LLVMRustOptimizeWithNewPassManager(
/*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
} }
); );
#endif
}
if (SanitizerOptions->SanitizeHWAddress) {
#if LLVM_VERSION_GE(11, 0)
OptimizerLastEPCallbacks.push_back(
[SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
MPM.addPass(HWAddressSanitizerPass(
/*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover));
}
);
#else
PipelineStartEPCallbacks.push_back(
[SanitizerOptions](ModulePassManager &MPM) {
MPM.addPass(HWAddressSanitizerPass(
/*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover));
}
);
#endif #endif
} }
} }

View file

@ -205,6 +205,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
return Attribute::ReadNone; return Attribute::ReadNone;
case InaccessibleMemOnly: case InaccessibleMemOnly:
return Attribute::InaccessibleMemOnly; return Attribute::InaccessibleMemOnly;
case SanitizeHWAddress:
return Attribute::SanitizeHWAddress;
} }
report_fatal_error("bad AttributeKind"); report_fatal_error("bad AttributeKind");
} }

View file

@ -43,6 +43,7 @@ bitflags! {
const LEAK = 1 << 1; const LEAK = 1 << 1;
const MEMORY = 1 << 2; const MEMORY = 1 << 2;
const THREAD = 1 << 3; const THREAD = 1 << 3;
const HWADDRESS = 1 << 4;
} }
} }
@ -56,6 +57,7 @@ impl fmt::Display for SanitizerSet {
SanitizerSet::LEAK => "leak", SanitizerSet::LEAK => "leak",
SanitizerSet::MEMORY => "memory", SanitizerSet::MEMORY => "memory",
SanitizerSet::THREAD => "thread", SanitizerSet::THREAD => "thread",
SanitizerSet::HWADDRESS => "hwaddress",
_ => panic!("unrecognized sanitizer {:?}", s), _ => panic!("unrecognized sanitizer {:?}", s),
}; };
if !first { if !first {
@ -73,12 +75,18 @@ impl IntoIterator for SanitizerSet {
type IntoIter = std::vec::IntoIter<SanitizerSet>; type IntoIter = std::vec::IntoIter<SanitizerSet>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
[SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] [
.iter() SanitizerSet::ADDRESS,
.copied() SanitizerSet::LEAK,
.filter(|&s| self.contains(s)) SanitizerSet::MEMORY,
.collect::<Vec<_>>() SanitizerSet::THREAD,
.into_iter() SanitizerSet::HWADDRESS,
]
.iter()
.copied()
.filter(|&s| self.contains(s))
.collect::<Vec<_>>()
.into_iter()
} }
} }

View file

@ -253,7 +253,7 @@ macro_rules! options {
pub const parse_passes: &str = "a space-separated list of passes, or `all`"; pub const parse_passes: &str = "a space-separated list of passes, or `all`";
pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`"; pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `hwaddress`, `leak`, `memory` or `thread`";
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
pub const parse_cfguard: &str = pub const parse_cfguard: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@ -476,6 +476,7 @@ macro_rules! options {
"leak" => SanitizerSet::LEAK, "leak" => SanitizerSet::LEAK,
"memory" => SanitizerSet::MEMORY, "memory" => SanitizerSet::MEMORY,
"thread" => SanitizerSet::THREAD, "thread" => SanitizerSet::THREAD,
"hwaddress" => SanitizerSet::HWADDRESS,
_ => return false, _ => return false,
} }
} }

View file

@ -1126,7 +1126,8 @@ impl Session {
self.opts.optimize != config::OptLevel::No self.opts.optimize != config::OptLevel::No
// AddressSanitizer uses lifetimes to detect use after scope bugs. // AddressSanitizer uses lifetimes to detect use after scope bugs.
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
} }
pub fn link_dead_code(&self) -> bool { pub fn link_dead_code(&self) -> bool {
@ -1562,6 +1563,8 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
"x86_64-unknown-freebsd", "x86_64-unknown-freebsd",
"x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu",
]; ];
const HWASAN_SUPPORTED_TARGETS: &[&str] =
&["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
// Sanitizers can only be used on some tested platforms. // Sanitizers can only be used on some tested platforms.
for s in sess.opts.debugging_opts.sanitizer { for s in sess.opts.debugging_opts.sanitizer {
@ -1570,6 +1573,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS, SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS,
SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS, SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS,
SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS, SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS,
SanitizerSet::HWADDRESS => HWASAN_SUPPORTED_TARGETS,
_ => panic!("unrecognized sanitizer {}", s), _ => panic!("unrecognized sanitizer {}", s),
}; };
if !supported_targets.contains(&&*sess.opts.target_triple.triple()) { if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {

View file

@ -593,6 +593,7 @@ symbols! {
html_no_source, html_no_source,
html_playground_url, html_playground_url,
html_root_url, html_root_url,
hwaddress,
i, i,
i128, i128,
i128_type, i128_type,

View file

@ -2628,10 +2628,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
} else if item.has_name(sym::thread) { } else if item.has_name(sym::thread) {
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
} else if item.has_name(sym::hwaddress) {
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
} else { } else {
tcx.sess tcx.sess
.struct_span_err(item.span(), "invalid argument for `no_sanitize`") .struct_span_err(item.span(), "invalid argument for `no_sanitize`")
.note("expected one of: `address`, `memory` or `thread`") .note("expected one of: `address`, `hwaddress`, `memory` or `thread`")
.emit(); .emit();
} }
} }

View file

@ -51,7 +51,7 @@ o("option-checking", None, "complain about unrecognized options in this configur
o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)") o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date") o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
o("vendor", "build.vendor", "enable usage of vendored Rust crates") o("vendor", "build.vendor", "enable usage of vendored Rust crates")
o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan)") o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan, hwasan)")
o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball") o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo") o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo")
o("profiler", "build.profiler", "build the profiler runtime") o("profiler", "build.profiler", "build the profiler runtime")

View file

@ -804,7 +804,7 @@ fn supported_sanitizers(
"aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
"aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
"aarch64-unknown-linux-gnu" => { "aarch64-unknown-linux-gnu" => {
common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan"]) common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
} }
"x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
"x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), "x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),

View file

@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize`
LL | #[no_sanitize(brontosaurus)] LL | #[no_sanitize(brontosaurus)]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
= note: expected one of: `address`, `memory` or `thread` = note: expected one of: `address`, `hwaddress`, `memory` or `thread`
error: aborting due to previous error error: aborting due to previous error

View file

@ -0,0 +1,19 @@
// needs-sanitizer-support
// needs-sanitizer-hwaddress
//
// compile-flags: -Z sanitizer=hwaddress -O -g
//
// run-fail
// error-pattern: HWAddressSanitizer: tag-mismatch
#![feature(test)]
use std::hint::black_box;
fn main() {
let xs = vec![0, 1, 2, 3];
// Avoid optimizing everything out.
let xs = black_box(xs.as_ptr());
let code = unsafe { *xs.offset(4) };
std::process::exit(code);
}

View file

@ -48,6 +48,7 @@ impl EarlyProps {
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target);
iter_header(testfile, None, rdr, &mut |ln| { iter_header(testfile, None, rdr, &mut |ln| {
// we should check if any only-<platform> exists and if it exists // we should check if any only-<platform> exists and if it exists
@ -101,6 +102,10 @@ impl EarlyProps {
props.ignore = true; props.ignore = true;
} }
if !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress") {
props.ignore = true;
}
if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) { if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
props.ignore = true; props.ignore = true;
} }

View file

@ -110,6 +110,9 @@ pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[
"x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu",
]; ];
pub const HWASAN_SUPPORTED_TARGETS: &[&str] =
&["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
const BIG_ENDIAN: &[&str] = &[ const BIG_ENDIAN: &[&str] = &[
"aarch64_be", "aarch64_be",
"armebv7r", "armebv7r",