Skip to content

Commit

Permalink
Rollup merge of #91675 - ivanloz:memtagsan, r=nagisa
Browse files Browse the repository at this point in the history
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
  • Loading branch information
matthiaskrgr authored Feb 18, 2022
2 parents f8b83a2 + 568aeda commit 0bb72a2
Show file tree
Hide file tree
Showing 15 changed files with 67 additions and 6 deletions.
13 changes: 13 additions & 0 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ pub enum Attribute {
StackProtectStrong = 31,
StackProtect = 32,
NoUndef = 33,
SanitizeMemTag = 34,
}

/// LLVMIntPredicate
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ enum LLVMRustAttribute {
StackProtectStrong = 31,
StackProtect = 32,
NoUndef = 33,
SanitizeMemTag = 34,
};

typedef struct OpaqueRustString *RustStringRef;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`";
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,7 @@ symbols! {
mem_zeroed,
member_constraints,
memory,
memtag,
message,
meta,
metadata_type,
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_target/src/spec/aarch64_linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ bitflags::bitflags! {
const THREAD = 1 << 3;
const HWADDRESS = 1 << 4;
const CFI = 1 << 5;
const MEMTAG = 1 << 6;
}
}

Expand All @@ -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,
Expand Down Expand Up @@ -652,6 +654,7 @@ impl IntoIterator for SanitizerSet {
SanitizerSet::CFI,
SanitizerSet::LEAK,
SanitizerSet::MEMORY,
SanitizerSet::MEMTAG,
SanitizerSet::THREAD,
SanitizerSet::HWADDRESS,
]
Expand Down Expand Up @@ -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)),
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3009,14 +3009,16 @@ 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) {
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`, `hwaddress`, `memory` or `thread`")
.note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`")
.emit();
}
}
Expand Down
20 changes: 18 additions & 2 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions src/test/codegen/sanitizer_memtag_attr_check.rs
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion src/test/ui/invalid/invalid-no-sanitize.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

2 changes: 2 additions & 0 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/tools/compiletest/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 0bb72a2

Please sign in to comment.