Skip to content

Commit 93ffde6

Browse files
committed
Auto merge of rust-lang#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.
2 parents 0e7c843 + adf61e3 commit 93ffde6

File tree

14 files changed

+57
-4
lines changed

14 files changed

+57
-4
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ pub fn sanitize_attrs<'ll>(
6969
if enabled.contains(SanitizerSet::HWADDRESS) {
7070
attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx));
7171
}
72+
if enabled.contains(SanitizerSet::SHADOWCALLSTACK) {
73+
attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx));
74+
}
7275
if enabled.contains(SanitizerSet::MEMTAG) {
7376
// Check to make sure the mte target feature is actually enabled.
7477
let features = cx.tcx.global_backend_features(());

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ pub enum AttributeKind {
192192
NoUndef = 33,
193193
SanitizeMemTag = 34,
194194
NoCfCheck = 35,
195+
ShadowCallStack = 36,
195196
}
196197

197198
/// LLVMIntPredicate

compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ enum LLVMRustAttribute {
8585
NoUndef = 33,
8686
SanitizeMemTag = 34,
8787
NoCfCheck = 35,
88+
ShadowCallStack = 36,
8889
};
8990

9091
typedef struct OpaqueRustString *RustStringRef;

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
232232
return Attribute::NoUndef;
233233
case SanitizeMemTag:
234234
return Attribute::SanitizeMemTag;
235+
case ShadowCallStack:
236+
return Attribute::ShadowCallStack;
235237
}
236238
report_fatal_error("bad AttributeKind");
237239
}

compiler/rustc_session/src/options.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ mod desc {
377377
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
378378
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
379379
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
380-
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, or `thread`";
380+
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
381381
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
382382
pub const parse_cfguard: &str =
383383
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@@ -683,6 +683,7 @@ mod parse {
683683
"leak" => SanitizerSet::LEAK,
684684
"memory" => SanitizerSet::MEMORY,
685685
"memtag" => SanitizerSet::MEMTAG,
686+
"shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
686687
"thread" => SanitizerSet::THREAD,
687688
"hwaddress" => SanitizerSet::HWADDRESS,
688689
_ => return false,

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,7 @@ symbols! {
12841284
self_in_typedefs,
12851285
self_struct_ctor,
12861286
semitransparent,
1287+
shadow_call_stack,
12871288
shl,
12881289
shl_assign,
12891290
should_panic,

compiler/rustc_target/src/spec/aarch64_linux_android.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub fn target() -> Target {
1717
supported_sanitizers: SanitizerSet::CFI
1818
| SanitizerSet::HWADDRESS
1919
| SanitizerSet::MEMTAG
20+
| SanitizerSet::SHADOWCALLSTACK
2021
| SanitizerSet::ADDRESS,
2122
..super::android_base::opts()
2223
},

compiler/rustc_target/src/spec/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ bitflags::bitflags! {
618618
const HWADDRESS = 1 << 4;
619619
const CFI = 1 << 5;
620620
const MEMTAG = 1 << 6;
621+
const SHADOWCALLSTACK = 1 << 7;
621622
}
622623
}
623624

@@ -632,6 +633,7 @@ impl SanitizerSet {
632633
SanitizerSet::LEAK => "leak",
633634
SanitizerSet::MEMORY => "memory",
634635
SanitizerSet::MEMTAG => "memtag",
636+
SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
635637
SanitizerSet::THREAD => "thread",
636638
SanitizerSet::HWADDRESS => "hwaddress",
637639
_ => return None,
@@ -666,6 +668,7 @@ impl IntoIterator for SanitizerSet {
666668
SanitizerSet::LEAK,
667669
SanitizerSet::MEMORY,
668670
SanitizerSet::MEMTAG,
671+
SanitizerSet::SHADOWCALLSTACK,
669672
SanitizerSet::THREAD,
670673
SanitizerSet::HWADDRESS,
671674
]
@@ -1960,6 +1963,7 @@ impl Target {
19601963
Some("leak") => SanitizerSet::LEAK,
19611964
Some("memory") => SanitizerSet::MEMORY,
19621965
Some("memtag") => SanitizerSet::MEMTAG,
1966+
Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
19631967
Some("thread") => SanitizerSet::THREAD,
19641968
Some("hwaddress") => SanitizerSet::HWADDRESS,
19651969
Some(s) => return Err(format!("unknown sanitizer {}", s)),

compiler/rustc_typeck/src/collect.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2974,14 +2974,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
29742974
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
29752975
} else if item.has_name(sym::memtag) {
29762976
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
2977+
} else if item.has_name(sym::shadow_call_stack) {
2978+
codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK;
29772979
} else if item.has_name(sym::thread) {
29782980
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
29792981
} else if item.has_name(sym::hwaddress) {
29802982
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
29812983
} else {
29822984
tcx.sess
29832985
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
2984-
.note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`")
2986+
.note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
29852987
.emit();
29862988
}
29872989
}

src/doc/unstable-book/src/compiler-flags/sanitizer.md

+16-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ This feature allows for use of one of following sanitizers:
1818
* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
1919
* [MemTagSanitizer][clang-memtag] fast memory error detector based on
2020
Armv8.5-A Memory Tagging Extension.
21+
* [ShadowCallStack][clang-scs] provides backward-edge control flow protection.
2122
* [ThreadSanitizer][clang-tsan] a fast data race detector.
2223

2324
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
2425
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`,
25-
`-Zsanitizer=memtag`, or `-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. Example:
26+
`-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or `-Zsanitizer=thread`.
27+
You might also need the `--target` and `build-std` flags. Example:
2628
```shell
2729
$ RUSTFLAGS=-Zsanitizer=address cargo build -Zbuild-std --target x86_64-unknown-linux-gnu
2830
```
@@ -513,6 +515,18 @@ To enable this target feature compile with `-C target-feature="+mte"`.
513515
514516
More information can be found in the associated [LLVM documentation](https://llvm.org/docs/MemTagSanitizer.html).
515517
518+
# ShadowCallStack
519+
520+
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.
521+
522+
ShadowCallStack requires a platform ABI which reserves `x18` as the instrumentation makes use of this register.
523+
524+
ShadowCallStack can be enabled with `-Zsanitizer=shadow-call-stack` option and is supported on the following targets:
525+
526+
* `aarch64-linux-android`
527+
528+
A runtime must be provided by the application or operating system. See the [LLVM documentation][clang-scs] for further details.
529+
516530
# ThreadSanitizer
517531
518532
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
610624
[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
611625
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
612626
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
627+
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
613628
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// This tests that the shadowcallstack attribute is
2+
// applied when enabling the shadow-call-stack sanitizer.
3+
//
4+
// needs-sanitizer-shadow-call-stack
5+
// compile-flags: -Zsanitizer=shadow-call-stack
6+
7+
#![crate_type = "lib"]
8+
#![feature(no_sanitize)]
9+
10+
// CHECK: ; Function Attrs:{{.*}}shadowcallstack
11+
// CHECK-NEXT: scs
12+
pub fn scs() {}
13+
14+
// CHECK-NOT: ; Function Attrs:{{.*}}shadowcallstack
15+
// CHECK-NEXT: no_scs
16+
#[no_sanitize(shadow_call_stack)]
17+
pub fn no_scs() {}

src/test/ui/invalid/invalid-no-sanitize.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize`
44
LL | #[no_sanitize(brontosaurus)]
55
| ^^^^^^^^^^^^
66
|
7-
= note: expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`
7+
= note: expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
88

99
error: aborting due to previous error
1010

src/tools/compiletest/src/header.rs

+3
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ pub fn make_test_description<R: Read>(
876876
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
877877
let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target);
878878
let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target);
879+
let has_shadow_call_stack = util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(&&*config.target);
879880
// for `-Z gcc-ld=lld`
880881
let has_rust_lld = config
881882
.compile_lib_path
@@ -913,6 +914,8 @@ pub fn make_test_description<R: Read>(
913914
ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");
914915
ignore |= !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress");
915916
ignore |= !has_memtag && config.parse_name_directive(ln, "needs-sanitizer-memtag");
917+
ignore |= !has_shadow_call_stack
918+
&& config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack");
916919
ignore |= config.target_panic == PanicStrategy::Abort
917920
&& config.parse_name_directive(ln, "needs-unwind");
918921
ignore |= config.target == "wasm32-unknown-unknown"

src/tools/compiletest/src/util.rs

+2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ pub const HWASAN_SUPPORTED_TARGETS: &[&str] =
121121
pub const MEMTAG_SUPPORTED_TARGETS: &[&str] =
122122
&["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
123123

124+
pub const SHADOWCALLSTACK_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-android"];
125+
124126
const BIG_ENDIAN: &[&str] = &[
125127
"aarch64_be",
126128
"armebv7r",

0 commit comments

Comments
 (0)