Rollup merge of #81506 - vo4:hwasan, r=nagisa
HWAddressSanitizer support # Motivation Compared to regular ASan, HWASan has a [smaller overhead](https://source.android.com/devices/tech/debug/hwasan). The difference in practice is that HWASan'ed code is more usable, e.g. Android device compiled with HWASan can be used as a daily driver. # Example ``` fn main() { let xs = vec![0, 1, 2, 3]; let _y = unsafe { *xs.as_ptr().offset(4) }; } ``` ``` ==223==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffff0050 at pc 0xaaaad00b3468 READ of size 4 at 0xefdeffff0050 tags: e5/00 (ptr/mem) in thread T0 #0 0xaaaad00b3464 (/root/main+0x53464) #1 0xaaaad00b39b4 (/root/main+0x539b4) #2 0xaaaad00b3dd0 (/root/main+0x53dd0) #3 0xaaaad00b61dc (/root/main+0x561dc) #4 0xaaaad00c0574 (/root/main+0x60574) #5 0xaaaad00b6290 (/root/main+0x56290) #6 0xaaaad00b6170 (/root/main+0x56170) #7 0xaaaad00b3578 (/root/main+0x53578) #8 0xffff81345e70 (/lib64/libc.so.6+0x20e70) #9 0xaaaad0096310 (/root/main+0x36310) [0xefdeffff0040,0xefdeffff0060) is a small allocated heap chunk; size: 32 offset: 16 0xefdeffff0050 is located 0 bytes to the right of 16-byte region [0xefdeffff0040,0xefdeffff0050) allocated here: #0 0xaaaad009bcdc (/root/main+0x3bcdc) #1 0xaaaad00b1eb0 (/root/main+0x51eb0) #2 0xaaaad00b20d4 (/root/main+0x520d4) #3 0xaaaad00b2800 (/root/main+0x52800) #4 0xaaaad00b1cf4 (/root/main+0x51cf4) #5 0xaaaad00b33d4 (/root/main+0x533d4) #6 0xaaaad00b39b4 (/root/main+0x539b4) #7 0xaaaad00b61dc (/root/main+0x561dc) #8 0xaaaad00b3578 (/root/main+0x53578) #9 0xaaaad0096310 (/root/main+0x36310) Thread: T0 0xeffe00002000 stack: [0xffffc0590000,0xffffc0d90000) sz: 8388608 tls: [0xffff81521020,0xffff815217d0) Memory tags around the buggy address (one tag corresponds to 16 bytes): 0xfefcefffef80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefcefffef90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefcefffefa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefcefffefb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefcefffefc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefcefffefd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefcefffefe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefcefffeff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0xfefceffff000: a2 a2 05 00 e5 [00] 00 00 00 00 00 00 00 00 00 00 0xfefceffff010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefceffff020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefceffff030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefceffff040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefceffff050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefceffff060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefceffff070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0xfefceffff080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Tags for short granules around the buggy address (one tag corresponds to 16 bytes): 0xfefcefffeff0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. =>0xfefceffff000: .. .. c5 .. .. [..] .. .. .. .. .. .. .. .. .. .. 0xfefceffff010: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags Registers where the failure occurred (pc 0xaaaad00b3468): x0 e500efdeffff0050 x1 0000000000000004 x2 0000ffffc0d8f5a0 x3 0200efff00000000 x4 0000ffffc0d8f4c0 x5 000000000000004f x6 00000ffffc0d8f36 x7 0000efff00000000 x8 e500efdeffff0050 x9 0200efff00000000 x10 0000000000000000 x11 0200efff00000000 x12 0200effe000006b0 x13 0200effe000006b0 x14 0000000000000008 x15 00000000c00000cf x16 0000aaaad00a0afc x17 0000000000000003 x18 0000000000000001 x19 0000ffffc0d8f718 x20 ba00ffffc0d8f7a0 x21 0000aaaad00962e0 x22 0000000000000000 x23 0000000000000000 x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000 x28 0000000000000000 x29 0000ffffc0d8f650 x30 0000aaaad00b3468 ``` # Comments/Caveats * HWASan is only supported on arm64. * I'm not sure if I should add a feature gate or piggyback on the existing one for sanitizers. * HWASan requires `-C target-feature=+tagged-globals`. That flag should probably be set transparently to the user. Not sure how to go about that. # TODO * Need more tests. * Update documentation. * Fix symbolization. * Integrate with CI
This commit is contained in:
commit
58d72aedee
19 changed files with 187 additions and 14 deletions
|
@ -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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -85,6 +85,7 @@ enum LLVMRustAttribute {
|
|||
ReturnsTwice = 25,
|
||||
ReadNone = 26,
|
||||
InaccessibleMemOnly = 27,
|
||||
SanitizeHWAddress = 28,
|
||||
};
|
||||
|
||||
typedef struct OpaqueRustString *RustStringRef;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -593,6 +593,7 @@ symbols! {
|
|||
html_no_source,
|
||||
html_playground_url,
|
||||
html_root_url,
|
||||
hwaddress,
|
||||
i,
|
||||
i128,
|
||||
i128_type,
|
||||
|
|
|
@ -2709,10 +2709,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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue