]> git.lizzy.rs Git - rust.git/commitdiff
Add ShadowCallStack Support
authorIvan Lozano <ivanlozano@google.com>
Fri, 17 Jun 2022 18:14:58 +0000 (14:14 -0400)
committerIvan Lozano <ivanlozano@google.com>
Wed, 20 Jul 2022 13:43:34 +0000 (13:43 +0000)
Adds support for the LLVM ShadowCallStack sanitizer.

14 files changed:
compiler/rustc_codegen_llvm/src/attributes.rs
compiler/rustc_codegen_llvm/src/llvm/ffi.rs
compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
compiler/rustc_session/src/options.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_target/src/spec/aarch64_linux_android.rs
compiler/rustc_target/src/spec/mod.rs
compiler/rustc_typeck/src/collect.rs
src/doc/unstable-book/src/compiler-flags/sanitizer.md
src/test/codegen/sanitizer_scs_attr_check.rs [new file with mode: 0644]
src/test/ui/invalid/invalid-no-sanitize.stderr
src/tools/compiletest/src/header.rs
src/tools/compiletest/src/util.rs

index 62da99ac3fb6605d10310c8d04ad5816e69176a5..1a96dd8bec47a606eef74a08ee39dd116522dbfc 100644 (file)
@@ -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(());
index 73cedb59349d3467b31eeba1e253d2e6a7e5b1be..8a6101e051b7c5bee7cde41a17e614c6ad1e0c19 100644 (file)
@@ -192,6 +192,7 @@ pub enum AttributeKind {
     NoUndef = 33,
     SanitizeMemTag = 34,
     NoCfCheck = 35,
+    ShadowCallStack = 36,
 }
 
 /// LLVMIntPredicate
index 9fe84a6309b9c1cc26af214ae87a93e362718048..bca5425e7287c947d10a29e53e2af42e8710c247 100644 (file)
@@ -85,6 +85,7 @@ enum LLVMRustAttribute {
   NoUndef = 33,
   SanitizeMemTag = 34,
   NoCfCheck = 35,
+  ShadowCallStack = 36,
 };
 
 typedef struct OpaqueRustString *RustStringRef;
index 2d35ee8976e67c6bd925b0429cd9bcc9578a4605..4615558b912984e4537e058c82e8aad292369e7f 100644 (file)
@@ -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");
 }
index 01ff9e254f792c02f8d6561185813b4266bf8a5b..8a0b4d14d50a1b19f0125489bd454288bb9e6b10 100644 (file)
@@ -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 @@ pub(crate) fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool
                     "leak" => SanitizerSet::LEAK,
                     "memory" => SanitizerSet::MEMORY,
                     "memtag" => SanitizerSet::MEMTAG,
+                    "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
                     "thread" => SanitizerSet::THREAD,
                     "hwaddress" => SanitizerSet::HWADDRESS,
                     _ => return false,
index 3ccd3fcbafa2bafb5c4db93540e3689f6176dc58..361facca31b0278ad717ea7f87956259ca1d6a21 100644 (file)
         self_in_typedefs,
         self_struct_ctor,
         semitransparent,
+        shadow_call_stack,
         shl,
         shl_assign,
         should_panic,
index 5e31859aaef304742a9160edd3fe34107d332a7e..c85f7f62a4239106b221e75a92571f728d6ad8ca 100644 (file)
@@ -17,6 +17,7 @@ pub fn target() -> Target {
             supported_sanitizers: SanitizerSet::CFI
                 | SanitizerSet::HWADDRESS
                 | SanitizerSet::MEMTAG
+                | SanitizerSet::SHADOWCALLSTACK
                 | SanitizerSet::ADDRESS,
             ..super::android_base::opts()
         },
index 1a6bb4a2eaf3432c1d6cc88676aa93dfd6646504..f7abeafd38f10d5870e037edca98ca230c0bd3c2 100644 (file)
@@ -618,6 +618,7 @@ pub struct SanitizerSet: u8 {
         const HWADDRESS = 1 << 4;
         const CFI     = 1 << 5;
         const MEMTAG  = 1 << 6;
+        const SHADOWCALLSTACK = 1 << 7;
     }
 }
 
@@ -632,6 +633,7 @@ pub fn as_str(self) -> Option<&'static str> {
             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 @@ fn into_iter(self) -> Self::IntoIter {
             SanitizerSet::LEAK,
             SanitizerSet::MEMORY,
             SanitizerSet::MEMTAG,
+            SanitizerSet::SHADOWCALLSTACK,
             SanitizerSet::THREAD,
             SanitizerSet::HWADDRESS,
         ]
@@ -1960,6 +1963,7 @@ macro_rules! key {
                                 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)),
index 6ec741269e8eb85a61dfb9c965fabe52e1765b38..a35c3430278234a5964ccbcc208d299c8fbb93d2 100644 (file)
@@ -2939,6 +2939,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) {
@@ -2946,7 +2948,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();
                     }
                 }
index e83c4d98cc7a4cad6edd2bfb4b4d5e442e03c391..9e11a154db9a66502a6b6fb3b05afb1aa6371226 100644 (file)
@@ -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 (file)
index 0000000..0b53db3
--- /dev/null
@@ -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() {}
index 5a92555eb322e9f6de60831790288115e09d39c0..d328cafa00b9345f51bfbaa1b26de8bef76a4355 100644 (file)
@@ -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
 
index 17f2b77dab052b65c97de24da48136ddde73dfc4..37643c29d4d0348c151daa9a0d5215f03428f171 100644 (file)
@@ -862,6 +862,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
@@ -899,6 +900,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"
index 215af347f17d219d09939a88e811101ea3334b8d..102b9308dc90c6e3d49712733b3afada838a34cf 100644 (file)
 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",