diff options
| author | bors <bors@rust-lang.org> | 2022-07-23 20:01:07 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-07-23 20:01:07 +0000 |
| commit | 93ffde6f04d3d24327a4e17a2a2bf4f63c246235 (patch) | |
| tree | 138c07c7746a3584c785e1f22d008805b342f4e0 | |
| parent | 0e7c843da4b7628b8becd96970f22b57dd400ace (diff) | |
| parent | adf61e3b2b72f4a06b3ac5cf90d49deda42da605 (diff) | |
| download | rust-93ffde6f04d3d24327a4e17a2a2bf4f63c246235.tar.gz rust-93ffde6f04d3d24327a4e17a2a2bf4f63c246235.zip | |
Auto merge of #98208 - ivanloz:master, r=nagisa
Add support for LLVM ShadowCallStack. LLVMs ShadowCallStack provides backward edge control flow integrity protection by using a separate shadow stack to store and retrieve a function's return address. LLVM currently only supports this for AArch64 targets. The x18 register is used to hold the pointer to the shadow stack, and therefore this only works on ABIs which reserve x18. Further details are available in the [LLVM ShadowCallStack](https://clang.llvm.org/docs/ShadowCallStack.html) docs. # Usage `-Zsanitizer=shadow-call-stack` # Comments/Caveats * Currently only enabled for the aarch64-linux-android target * Requires the platform to define a runtime to initialize the shadow stack, see the [LLVM docs](https://clang.llvm.org/docs/ShadowCallStack.html) for more detail.
| -rw-r--r-- | compiler/rustc_codegen_llvm/src/attributes.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h | 1 | ||||
| -rw-r--r-- | compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 2 | ||||
| -rw-r--r-- | compiler/rustc_session/src/options.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_target/src/spec/aarch64_linux_android.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_target/src/spec/mod.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/collect.rs | 4 | ||||
| -rw-r--r-- | src/doc/unstable-book/src/compiler-flags/sanitizer.md | 17 | ||||
| -rw-r--r-- | src/test/codegen/sanitizer_scs_attr_check.rs | 17 | ||||
| -rw-r--r-- | src/test/ui/invalid/invalid-no-sanitize.stderr | 2 | ||||
| -rw-r--r-- | src/tools/compiletest/src/header.rs | 3 | ||||
| -rw-r--r-- | src/tools/compiletest/src/util.rs | 2 |
14 files changed, 57 insertions, 4 deletions
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 62da99ac3fb..1a96dd8bec4 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -69,6 +69,9 @@ pub fn sanitize_attrs<'ll>( if enabled.contains(SanitizerSet::HWADDRESS) { attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx)); } + if enabled.contains(SanitizerSet::SHADOWCALLSTACK) { + attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx)); + } if enabled.contains(SanitizerSet::MEMTAG) { // Check to make sure the mte target feature is actually enabled. let features = cx.tcx.global_backend_features(()); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 624ce8d9369..f64eb79b0a8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -192,6 +192,7 @@ pub enum AttributeKind { NoUndef = 33, SanitizeMemTag = 34, NoCfCheck = 35, + ShadowCallStack = 36, } /// LLVMIntPredicate diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 9fe84a6309b..bca5425e728 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -85,6 +85,7 @@ enum LLVMRustAttribute { NoUndef = 33, SanitizeMemTag = 34, NoCfCheck = 35, + ShadowCallStack = 36, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2d35ee8976e..4615558b912 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -232,6 +232,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::NoUndef; case SanitizeMemTag: return Attribute::SanitizeMemTag; + case ShadowCallStack: + return Attribute::ShadowCallStack; } report_fatal_error("bad AttributeKind"); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 21b1b0b4ebf..1b583417ca0 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -377,7 +377,7 @@ mod desc { pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_oom_strategy: &str = "either `panic` 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`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, 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`"; @@ -683,6 +683,7 @@ mod parse { "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, "memtag" => SanitizerSet::MEMTAG, + "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK, "thread" => SanitizerSet::THREAD, "hwaddress" => SanitizerSet::HWADDRESS, _ => return false, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d15b15f75dd..cb15132bd4b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1284,6 +1284,7 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + shadow_call_stack, shl, shl_assign, should_panic, diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index 5e31859aaef..c85f7f62a42 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -17,6 +17,7 @@ pub fn target() -> Target { supported_sanitizers: SanitizerSet::CFI | SanitizerSet::HWADDRESS | SanitizerSet::MEMTAG + | SanitizerSet::SHADOWCALLSTACK | SanitizerSet::ADDRESS, ..super::android_base::opts() }, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1a6bb4a2eaf..f7abeafd38f 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -618,6 +618,7 @@ bitflags::bitflags! { const HWADDRESS = 1 << 4; const CFI = 1 << 5; const MEMTAG = 1 << 6; + const SHADOWCALLSTACK = 1 << 7; } } @@ -632,6 +633,7 @@ impl SanitizerSet { SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", SanitizerSet::MEMTAG => "memtag", + SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack", SanitizerSet::THREAD => "thread", SanitizerSet::HWADDRESS => "hwaddress", _ => return None, @@ -666,6 +668,7 @@ impl IntoIterator for SanitizerSet { SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::MEMTAG, + SanitizerSet::SHADOWCALLSTACK, SanitizerSet::THREAD, SanitizerSet::HWADDRESS, ] @@ -1960,6 +1963,7 @@ impl Target { Some("leak") => SanitizerSet::LEAK, Some("memory") => SanitizerSet::MEMORY, Some("memtag") => SanitizerSet::MEMTAG, + Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK, Some("thread") => SanitizerSet::THREAD, Some("hwaddress") => SanitizerSet::HWADDRESS, Some(s) => return Err(format!("unknown sanitizer {}", s)), diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index c303905652d..c562599e2cc 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -2974,6 +2974,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { 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::shadow_call_stack) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK; } else if item.has_name(sym::thread) { codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; } else if item.has_name(sym::hwaddress) { @@ -2981,7 +2983,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`") + .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`") .emit(); } } diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index e83c4d98cc7..9e11a154db9 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -18,11 +18,13 @@ This feature allows for use of one of following sanitizers: * [MemorySanitizer][clang-msan] a detector of uninitialized reads. * [MemTagSanitizer][clang-memtag] fast memory error detector based on Armv8.5-A Memory Tagging Extension. +* [ShadowCallStack][clang-scs] provides backward-edge control flow protection. * [ThreadSanitizer][clang-tsan] a fast data race detector. To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`, `-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`, -`-Zsanitizer=memtag`, or `-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. Example: +`-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or `-Zsanitizer=thread`. +You might also need the `--target` and `build-std` flags. Example: ```shell $ RUSTFLAGS=-Zsanitizer=address cargo build -Zbuild-std --target x86_64-unknown-linux-gnu ``` @@ -513,6 +515,18 @@ 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). +# ShadowCallStack + +ShadowCallStack provides backward edge control flow protection by storing a function's return address in a separately allocated 'shadow call stack' and loading the return address from that shadow call stack. + +ShadowCallStack requires a platform ABI which reserves `x18` as the instrumentation makes use of this register. + +ShadowCallStack can be enabled with `-Zsanitizer=shadow-call-stack` option and is supported on the following targets: + +* `aarch64-linux-android` + +A runtime must be provided by the application or operating system. See the [LLVM documentation][clang-scs] for further details. + # ThreadSanitizer ThreadSanitizer is a data race detection tool. It is supported on the following @@ -610,4 +624,5 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT [clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html +[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html diff --git a/src/test/codegen/sanitizer_scs_attr_check.rs b/src/test/codegen/sanitizer_scs_attr_check.rs new file mode 100644 index 00000000000..0b53db3b767 --- /dev/null +++ b/src/test/codegen/sanitizer_scs_attr_check.rs @@ -0,0 +1,17 @@ +// This tests that the shadowcallstack attribute is +// applied when enabling the shadow-call-stack sanitizer. +// +// needs-sanitizer-shadow-call-stack +// compile-flags: -Zsanitizer=shadow-call-stack + +#![crate_type = "lib"] +#![feature(no_sanitize)] + +// CHECK: ; Function Attrs:{{.*}}shadowcallstack +// CHECK-NEXT: scs +pub fn scs() {} + +// CHECK-NOT: ; Function Attrs:{{.*}}shadowcallstack +// CHECK-NEXT: no_scs +#[no_sanitize(shadow_call_stack)] +pub fn no_scs() {} diff --git a/src/test/ui/invalid/invalid-no-sanitize.stderr b/src/test/ui/invalid/invalid-no-sanitize.stderr index 5a92555eb32..d328cafa00b 100644 --- a/src/test/ui/invalid/invalid-no-sanitize.stderr +++ b/src/test/ui/invalid/invalid-no-sanitize.stderr @@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize` LL | #[no_sanitize(brontosaurus)] | ^^^^^^^^^^^^ | - = note: expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread` + = note: expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread` error: aborting due to previous error diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 02f4d29a2f0..15d82e6d596 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -876,6 +876,7 @@ pub fn make_test_description<R: Read>( 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); + let has_shadow_call_stack = util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(&&*config.target); // for `-Z gcc-ld=lld` let has_rust_lld = config .compile_lib_path @@ -913,6 +914,8 @@ pub fn make_test_description<R: Read>( 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 |= !has_shadow_call_stack + && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack"); ignore |= config.target_panic == PanicStrategy::Abort && config.parse_name_directive(ln, "needs-unwind"); ignore |= config.target == "wasm32-unknown-unknown" diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 215af347f17..102b9308dc9 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -121,6 +121,8 @@ pub const HWASAN_SUPPORTED_TARGETS: &[&str] = pub const MEMTAG_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-android", "aarch64-unknown-linux-gnu"]; +pub const SHADOWCALLSTACK_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-android"]; + const BIG_ENDIAN: &[&str] = &[ "aarch64_be", "armebv7r", |
