Rollup merge of #91675 - ivanloz:memtagsan, r=nagisa
Add MemTagSanitizer Support Add support for the LLVM [MemTagSanitizer](https://llvm.org/docs/MemTagSanitizer.html). On hardware which supports it (see caveats below), the MemTagSanitizer can catch bugs similar to AddressSanitizer and HardwareAddressSanitizer, but with lower overhead. On a tag mismatch, a SIGSEGV is signaled with code SEGV_MTESERR / SEGV_MTEAERR. # Usage `-Zsanitizer=memtag -C target-feature="+mte"` # Comments/Caveats * MemTagSanitizer is only supported on AArch64 targets with hardware support * Requires `-C target-feature="+mte"` * LLVM MemTagSanitizer currently only performs stack tagging. # TODO * Tests * Example
This commit is contained in:
commit
0bb72a2c66
@ -55,6 +55,19 @@ pub fn sanitize<'ll>(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &
|
||||
if enabled.contains(SanitizerSet::HWADDRESS) {
|
||||
llvm::Attribute::SanitizeHWAddress.apply_llfn(Function, llfn);
|
||||
}
|
||||
if enabled.contains(SanitizerSet::MEMTAG) {
|
||||
// Check to make sure the mte target feature is actually enabled.
|
||||
let sess = cx.tcx.sess;
|
||||
let features = llvm_util::llvm_global_features(sess).join(",");
|
||||
let mte_feature_enabled = features.rfind("+mte");
|
||||
let mte_feature_disabled = features.rfind("-mte");
|
||||
|
||||
if mte_feature_enabled.is_none() || (mte_feature_disabled > mte_feature_enabled) {
|
||||
sess.err("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`");
|
||||
}
|
||||
|
||||
llvm::Attribute::SanitizeMemTag.apply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
|
||||
|
@ -190,6 +190,7 @@ pub enum Attribute {
|
||||
StackProtectStrong = 31,
|
||||
StackProtect = 32,
|
||||
NoUndef = 33,
|
||||
SanitizeMemTag = 34,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
|
@ -83,6 +83,7 @@ enum LLVMRustAttribute {
|
||||
StackProtectStrong = 31,
|
||||
StackProtect = 32,
|
||||
NoUndef = 33,
|
||||
SanitizeMemTag = 34,
|
||||
};
|
||||
|
||||
typedef struct OpaqueRustString *RustStringRef;
|
||||
|
@ -226,6 +226,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
|
||||
return Attribute::StackProtect;
|
||||
case NoUndef:
|
||||
return Attribute::NoUndef;
|
||||
case SanitizeMemTag:
|
||||
return Attribute::SanitizeMemTag;
|
||||
}
|
||||
report_fatal_error("bad AttributeKind");
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ mod desc {
|
||||
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
|
||||
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
|
||||
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory` or `thread`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, 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`";
|
||||
@ -639,6 +639,7 @@ mod parse {
|
||||
"cfi" => SanitizerSet::CFI,
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"memtag" => SanitizerSet::MEMTAG,
|
||||
"thread" => SanitizerSet::THREAD,
|
||||
"hwaddress" => SanitizerSet::HWADDRESS,
|
||||
_ => return false,
|
||||
|
@ -876,6 +876,7 @@ symbols! {
|
||||
mem_zeroed,
|
||||
member_constraints,
|
||||
memory,
|
||||
memtag,
|
||||
message,
|
||||
meta,
|
||||
metadata_type,
|
||||
|
@ -14,7 +14,9 @@ pub fn target() -> Target {
|
||||
// As documented in https://developer.android.com/ndk/guides/cpu-features.html
|
||||
// the neon (ASIMD) and FP must exist on all android aarch64 targets.
|
||||
features: "+neon,+fp-armv8".to_string(),
|
||||
supported_sanitizers: SanitizerSet::CFI | SanitizerSet::HWADDRESS,
|
||||
supported_sanitizers: SanitizerSet::CFI
|
||||
| SanitizerSet::HWADDRESS
|
||||
| SanitizerSet::MEMTAG,
|
||||
..super::android_base::opts()
|
||||
},
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ pub fn target() -> Target {
|
||||
| SanitizerSet::CFI
|
||||
| SanitizerSet::LEAK
|
||||
| SanitizerSet::MEMORY
|
||||
| SanitizerSet::MEMTAG
|
||||
| SanitizerSet::THREAD
|
||||
| SanitizerSet::HWADDRESS,
|
||||
..super::linux_gnu_base::opts()
|
||||
|
@ -606,6 +606,7 @@ bitflags::bitflags! {
|
||||
const THREAD = 1 << 3;
|
||||
const HWADDRESS = 1 << 4;
|
||||
const CFI = 1 << 5;
|
||||
const MEMTAG = 1 << 6;
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,6 +620,7 @@ impl SanitizerSet {
|
||||
SanitizerSet::CFI => "cfi",
|
||||
SanitizerSet::LEAK => "leak",
|
||||
SanitizerSet::MEMORY => "memory",
|
||||
SanitizerSet::MEMTAG => "memtag",
|
||||
SanitizerSet::THREAD => "thread",
|
||||
SanitizerSet::HWADDRESS => "hwaddress",
|
||||
_ => return None,
|
||||
@ -652,6 +654,7 @@ impl IntoIterator for SanitizerSet {
|
||||
SanitizerSet::CFI,
|
||||
SanitizerSet::LEAK,
|
||||
SanitizerSet::MEMORY,
|
||||
SanitizerSet::MEMTAG,
|
||||
SanitizerSet::THREAD,
|
||||
SanitizerSet::HWADDRESS,
|
||||
]
|
||||
@ -1883,6 +1886,7 @@ impl Target {
|
||||
Some("cfi") => SanitizerSet::CFI,
|
||||
Some("leak") => SanitizerSet::LEAK,
|
||||
Some("memory") => SanitizerSet::MEMORY,
|
||||
Some("memtag") => SanitizerSet::MEMTAG,
|
||||
Some("thread") => SanitizerSet::THREAD,
|
||||
Some("hwaddress") => SanitizerSet::HWADDRESS,
|
||||
Some(s) => return Err(format!("unknown sanitizer {}", s)),
|
||||
|
@ -3009,6 +3009,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
|
||||
} else if item.has_name(sym::memory) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
||||
} else if item.has_name(sym::memtag) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
|
||||
} else if item.has_name(sym::thread) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
|
||||
} else if item.has_name(sym::hwaddress) {
|
||||
@ -3016,7 +3018,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
} else {
|
||||
tcx.sess
|
||||
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
||||
.note("expected one of: `address`, `hwaddress`, `memory` or `thread`")
|
||||
.note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,13 @@ This feature allows for use of one of following sanitizers:
|
||||
AddressSanitizer, but based on partial hardware assistance.
|
||||
* [LeakSanitizer][clang-lsan] a run-time memory leak detector.
|
||||
* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
|
||||
* [MemTagSanitizer][clang-memtag] fast memory error detector based on
|
||||
Armv8.5-A Memory Tagging Extension.
|
||||
* [ThreadSanitizer][clang-tsan] a fast data race detector.
|
||||
|
||||
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
|
||||
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or
|
||||
`-Zsanitizer=thread`.
|
||||
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`,
|
||||
`-Zsanitizer=memtag`, or `-Zsanitizer=thread`.
|
||||
|
||||
# AddressSanitizer
|
||||
|
||||
@ -494,6 +496,20 @@ $ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu
|
||||
#0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3
|
||||
```
|
||||
|
||||
# MemTagSanitizer
|
||||
|
||||
MemTagSanitizer detects a similar class of errors as AddressSanitizer and HardwareAddressSanitizer, but with lower overhead suitable for use as hardening for production binaries.
|
||||
|
||||
MemTagSanitizer is supported on the following targets:
|
||||
|
||||
* `aarch64-linux-android`
|
||||
* `aarch64-unknown-linux-gnu`
|
||||
|
||||
MemTagSanitizer requires hardware support and the `mte` target feature.
|
||||
To enable this target feature compile with `-C target-feature="+mte"`.
|
||||
|
||||
More information can be found in the associated [LLVM documentation](https://llvm.org/docs/MemTagSanitizer.html).
|
||||
|
||||
# ThreadSanitizer
|
||||
|
||||
ThreadSanitizer is a data race detection tool. It is supported on the following
|
||||
|
12
src/test/codegen/sanitizer_memtag_attr_check.rs
Normal file
12
src/test/codegen/sanitizer_memtag_attr_check.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// This tests that the sanitize_memtag attribute is
|
||||
// applied when enabling the memtag sanitizer.
|
||||
//
|
||||
// needs-sanitizer-memtag
|
||||
// compile-flags: -Zsanitizer=memtag -Ctarget-feature=+mte
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// CHECK: ; Function Attrs:{{.*}}sanitize_memtag
|
||||
pub fn tagged() {}
|
||||
|
||||
// CHECK: attributes #0 = {{.*}}sanitize_memtag
|
@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize`
|
||||
LL | #[no_sanitize(brontosaurus)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected one of: `address`, `hwaddress`, `memory` or `thread`
|
||||
= note: expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -863,6 +863,7 @@ pub fn make_test_description<R: Read>(
|
||||
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);
|
||||
let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
// for `-Z gcc-ld=lld`
|
||||
let has_rust_lld = config
|
||||
.compile_lib_path
|
||||
@ -899,6 +900,7 @@ pub fn make_test_description<R: Read>(
|
||||
ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
|
||||
ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");
|
||||
ignore |= !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress");
|
||||
ignore |= !has_memtag && config.parse_name_directive(ln, "needs-sanitizer-memtag");
|
||||
ignore |= config.target_panic == PanicStrategy::Abort
|
||||
&& config.parse_name_directive(ln, "needs-unwind");
|
||||
ignore |= config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln);
|
||||
|
@ -117,6 +117,9 @@ pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[
|
||||
pub const HWASAN_SUPPORTED_TARGETS: &[&str] =
|
||||
&["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
|
||||
|
||||
pub const MEMTAG_SUPPORTED_TARGETS: &[&str] =
|
||||
&["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
|
||||
|
||||
const BIG_ENDIAN: &[&str] = &[
|
||||
"aarch64_be",
|
||||
"armebv7r",
|
||||
|
Loading…
x
Reference in New Issue
Block a user