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) {
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.

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_track_origins: config.sanitizer_memory_track_origins as c_int,
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS),
})
} else {
None
@ -652,6 +654,10 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static
if config.sanitizer.contains(SanitizerSet::THREAD) {
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(

View File

@ -131,6 +131,7 @@ pub enum Attribute {
ReturnsTwice = 25,
ReadNone = 26,
InaccessibleMemOnly = 27,
SanitizeHWAddress = 28,
}
/// LLVMIntPredicate
@ -439,6 +440,8 @@ pub struct SanitizerOptions {
pub sanitize_memory_recover: bool,
pub sanitize_memory_track_origins: c_int,
pub sanitize_thread: bool,
pub sanitize_hwaddress: bool,
pub sanitize_hwaddress_recover: bool,
}
/// LLVMRelocMode
@ -2128,6 +2131,7 @@ extern "C" {
Recover: bool,
) -> &'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 LLVMRustAddLastExtensionPasses(
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) {
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) {

View File

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

View File

@ -33,6 +33,7 @@
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
#include "llvm/Transforms/Utils/NameAnonGlobals.h"
@ -133,6 +134,12 @@ extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
return wrap(createThreadSanitizerLegacyPassPass());
}
extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) {
const bool CompileKernel = false;
return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover));
}
extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
assert(RustPass);
Pass *Pass = unwrap(RustPass);
@ -722,6 +729,8 @@ struct LLVMRustSanitizerOptions {
bool SanitizeMemoryRecover;
int SanitizeMemoryTrackOrigins;
bool SanitizeThread;
bool SanitizeHWAddress;
bool SanitizeHWAddressRecover;
};
extern "C" void
@ -886,6 +895,23 @@ LLVMRustOptimizeWithNewPassManager(
/*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
}
}

View File

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

View File

@ -43,6 +43,7 @@ bitflags! {
const LEAK = 1 << 1;
const MEMORY = 1 << 2;
const THREAD = 1 << 3;
const HWADDRESS = 1 << 4;
}
}
@ -56,6 +57,7 @@ impl fmt::Display for SanitizerSet {
SanitizerSet::LEAK => "leak",
SanitizerSet::MEMORY => "memory",
SanitizerSet::THREAD => "thread",
SanitizerSet::HWADDRESS => "hwaddress",
_ => panic!("unrecognized sanitizer {:?}", s),
};
if !first {
@ -73,12 +75,18 @@ impl IntoIterator for SanitizerSet {
type IntoIter = std::vec::IntoIter<SanitizerSet>;
fn into_iter(self) -> Self::IntoIter {
[SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD]
.iter()
.copied()
.filter(|&s| self.contains(s))
.collect::<Vec<_>>()
.into_iter()
[
SanitizerSet::ADDRESS,
SanitizerSet::LEAK,
SanitizerSet::MEMORY,
SanitizerSet::THREAD,
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_panic_strategy: &str = "either `unwind` or `abort`";
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_cfguard: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@ -476,6 +476,7 @@ macro_rules! options {
"leak" => SanitizerSet::LEAK,
"memory" => SanitizerSet::MEMORY,
"thread" => SanitizerSet::THREAD,
"hwaddress" => SanitizerSet::HWADDRESS,
_ => return false,
}
}

View File

@ -1126,7 +1126,8 @@ impl Session {
self.opts.optimize != config::OptLevel::No
// AddressSanitizer uses lifetimes to detect use after scope bugs.
// 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 {
@ -1562,6 +1563,8 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
"x86_64-unknown-freebsd",
"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.
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::MEMORY => MSAN_SUPPORTED_TARGETS,
SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS,
SanitizerSet::HWADDRESS => HWASAN_SUPPORTED_TARGETS,
_ => panic!("unrecognized sanitizer {}", s),
};
if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {

View File

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

View File

@ -2628,10 +2628,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
} else if item.has_name(sym::thread) {
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
} else if item.has_name(sym::hwaddress) {
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
} else {
tcx.sess
.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();
}
}

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("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
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("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo")
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-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
"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-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),

View File

@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize`
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

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_msan = util::MSAN_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| {
// we should check if any only-<platform> exists and if it exists
@ -101,6 +102,10 @@ impl EarlyProps {
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) {
props.ignore = true;
}

View File

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