Skip to content

Commit 019d75b

Browse files
committed
Add SafeStack support to rustc
Adds support for LLVM [SafeStack] which provides backward edge control flow protection by separating the stack into two parts: data which is only accessed in provable safe ways is allocated on the normal stack (the "safe stack") and all other data is placed in a separate allocation (the "unsafe stack"). SafeStack support is enabled by passing `-Zsanitizer=safestack`. [SafeStack]: https://clang.llvm.org/docs/SafeStack.html
1 parent d22314e commit 019d75b

File tree

14 files changed

+71
-10
lines changed

14 files changed

+71
-10
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ pub fn sanitize_attrs<'ll>(
8888

8989
attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
9090
}
91+
if enabled.contains(SanitizerSet::SAFESTACK) {
92+
attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
93+
}
9194
attrs
9295
}
9396

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ pub enum AttributeKind {
196196
AllocSize = 37,
197197
AllocatedPointer = 38,
198198
AllocAlign = 39,
199+
SanitizeSafeStack = 40,
199200
}
200201

201202
/// LLVMIntPredicate

compiler/rustc_codegen_ssa/src/back/link.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,9 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d
11881188
if sanitizer.contains(SanitizerSet::HWADDRESS) {
11891189
link_sanitizer_runtime(sess, linker, "hwasan");
11901190
}
1191+
if sanitizer.contains(SanitizerSet::SAFESTACK) {
1192+
link_sanitizer_runtime(sess, linker, "safestack");
1193+
}
11911194
}
11921195

11931196
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {

compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ enum LLVMRustAttribute {
9696
AllocatedPointer = 38,
9797
AllocAlign = 39,
9898
#endif
99+
SanitizeSafeStack = 40,
99100
};
100101

101102
typedef struct OpaqueRustString *RustStringRef;

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
234234
case AllocAlign:
235235
return Attribute::AllocAlign;
236236
#endif
237+
case SanitizeSafeStack:
238+
return Attribute::SafeStack;
237239
}
238240
report_fatal_error("bad AttributeKind");
239241
}

compiler/rustc_session/src/options.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ mod desc {
372372
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
373373
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
374374
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
375-
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
375+
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, or `thread`";
376376
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
377377
pub const parse_cfguard: &str =
378378
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@@ -694,6 +694,7 @@ mod parse {
694694
"shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
695695
"thread" => SanitizerSet::THREAD,
696696
"hwaddress" => SanitizerSet::HWADDRESS,
697+
"safestack" => SanitizerSet::SAFESTACK,
697698
_ => return false,
698699
}
699700
}

compiler/rustc_target/src/spec/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,7 @@ bitflags::bitflags! {
815815
const SHADOWCALLSTACK = 1 << 7;
816816
const KCFI = 1 << 8;
817817
const KERNELADDRESS = 1 << 9;
818+
const SAFESTACK = 1 << 10;
818819
}
819820
}
820821

@@ -831,6 +832,7 @@ impl SanitizerSet {
831832
SanitizerSet::LEAK => "leak",
832833
SanitizerSet::MEMORY => "memory",
833834
SanitizerSet::MEMTAG => "memtag",
835+
SanitizerSet::SAFESTACK => "safestack",
834836
SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
835837
SanitizerSet::THREAD => "thread",
836838
SanitizerSet::HWADDRESS => "hwaddress",
@@ -871,6 +873,7 @@ impl IntoIterator for SanitizerSet {
871873
SanitizerSet::THREAD,
872874
SanitizerSet::HWADDRESS,
873875
SanitizerSet::KERNELADDRESS,
876+
SanitizerSet::SAFESTACK,
874877
]
875878
.iter()
876879
.copied()
@@ -2364,6 +2367,7 @@ impl Target {
23642367
Some("leak") => SanitizerSet::LEAK,
23652368
Some("memory") => SanitizerSet::MEMORY,
23662369
Some("memtag") => SanitizerSet::MEMTAG,
2370+
Some("safestack") => SanitizerSet::SAFESTACK,
23672371
Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
23682372
Some("thread") => SanitizerSet::THREAD,
23692373
Some("hwaddress") => SanitizerSet::HWADDRESS,

compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub fn target() -> Target {
1111
| SanitizerSet::CFI
1212
| SanitizerSet::LEAK
1313
| SanitizerSet::MEMORY
14+
| SanitizerSet::SAFESTACK
1415
| SanitizerSet::THREAD;
1516
base.supports_xray = true;
1617

src/bootstrap/llvm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ fn supported_sanitizers(
10171017
"x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
10181018
"x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
10191019
"x86_64-unknown-linux-gnu" => {
1020-
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1020+
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "safestack", "tsan"])
10211021
}
10221022
"x86_64-unknown-linux-musl" => {
10231023
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])

src/doc/rustc/src/exploit-mitigations.md

+20-7
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ equivalent.
6666
| Heap corruption protection | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
6767
| Stack smashing protection | Yes | Nightly |
6868
| Forward-edge control flow protection | Yes | Nightly |
69-
| Backward-edge control flow protection (e.g., shadow and safe stack) | No | |
69+
| Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | Nightly |
7070

7171
<small id="fn:1">1\. See
7272
<https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec>
@@ -443,20 +443,21 @@ Newer processors provide hardware assistance for backward-edge control flow
443443
protection, such as ARM Pointer Authentication, and Intel Shadow Stack as
444444
part of Intel CET.
445445

446-
The Rust compiler does not support shadow or safe stack. There is work
447-
currently ongoing to add support for the sanitizers[40], which may or may
448-
not include support for safe stack<sup id="fnref:7" role="doc-noteref"><a
449-
href="#fn:7" class="footnote">7</a></sup>.
446+
The Rust compiler supports shadow stack for aarch64 only
447+
<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup>
448+
on nightly Rust compilers [43]-[44]. Safe stack is available on nightly
449+
Rust compilers [45]-[46].
450450

451451
```text
452452
$ readelf -s target/release/hello-rust | grep __safestack_init
453+
1177: 00000000000057b0 444 FUNC GLOBAL DEFAULT 9 __safestack_init
453454
```
454455
Fig. 16. Checking if LLVM SafeStack is enabled for a given binary.
455456

456457
The presence of the `__safestack_init` symbol indicates that LLVM SafeStack
457-
is enabled for a given binary. Conversely, the absence of the
458+
is enabled for a given binary (see Fig. 16). Conversely, the absence of the
458459
`__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a
459-
given binary (see Fig. 16).
460+
given binary.
460461

461462
<small id="fn:7">7\. The shadow stack implementation for the AMD64
462463
architecture and equivalent in LLVM was removed due to performance and
@@ -628,3 +629,15 @@ defaults (unrelated to `READ_IMPLIES_EXEC`).
628629

629630
42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.”
630631
GitHub. <https://github.com/rust-lang/rust/pull/84197>
632+
633+
43. ivanloz. “Add support for LLVM ShadowCallStack. #98208.” GitHub.
634+
<https://github.com/rust-lang/rust/pull/98208>.
635+
636+
44. “ShadowCallStack.” The Rust Unstable Book.
637+
[https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#shadowcallstack](../unstable-book/compiler-flags/sanitizer.html#shadowcallstack).
638+
639+
45. W. Wiser. “Add support for LLVM SafeStack #112000” GitHub.
640+
<https://github.com/rust-lang/rust/pull/112000>
641+
642+
46. “SafeStack.” The Rust Unstable Book.
643+
[https://doc.rust-lang/org/unstable-book/compiler-flags/sanitizer.html#safestack](../unstable-book/compiler-flags/sanitizer.html#safestack).

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

+13-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ This feature allows for use of one of following sanitizers:
2121
* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
2222
* [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on
2323
Armv8.5-A Memory Tagging Extension.
24-
* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow protection.
24+
* [SafeStack](#safestack) provides backward-edge control flow protection by separating the stack into safe and unsafe regions.
25+
* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow protection (aarch64 only).
2526
* [ThreadSanitizer](#threadsanitizer) a fast data race detector.
2627

2728
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
@@ -712,6 +713,16 @@ To enable this target feature compile with `-C target-feature="+mte"`.
712713
713714
See the [LLVM MemTagSanitizer documentation][llvm-memtag] for more details.
714715
716+
# SafeStack
717+
718+
SafeStack provides backward edge control flow protection by separating the stack into data which is only accessed safely (the safe stack) and all other data (the unsafe stack).
719+
720+
SafeStack can be enabled with the `-Zsanitizer=safestack` option and is supported on the following targets:
721+
722+
* `x86_64-unknown-linux-gnu`
723+
724+
See the [Clang SafeStack documentation][clang-safestack] for more details.
725+
715726
# ShadowCallStack
716727
717728
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.
@@ -828,6 +839,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
828839
[clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi
829840
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
830841
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
842+
[clang-safestack]: https://clang.llvm.org/docs/SafeStack.html
831843
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
832844
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
833845
[linux-kasan]: https://www.kernel.org/doc/html/latest/dev-tools/kasan.html

src/tools/compiletest/src/header/needs.rs

+7
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ pub(super) fn handle_needs(
7070
condition: cache.sanitizer_shadow_call_stack,
7171
ignore_reason: "ignored on targets without shadow call stacks",
7272
},
73+
Need {
74+
name: "needs-sanitizer-safestack",
75+
condition: cache.sanitizer_safestack,
76+
ignore_reason: "ignored on targets without SafeStack support",
77+
},
7378
Need {
7479
name: "needs-run-enabled",
7580
condition: config.run_enabled(),
@@ -184,6 +189,7 @@ pub(super) struct CachedNeedsConditions {
184189
sanitizer_hwaddress: bool,
185190
sanitizer_memtag: bool,
186191
sanitizer_shadow_call_stack: bool,
192+
sanitizer_safestack: bool,
187193
xray: bool,
188194
rust_lld: bool,
189195
i686_dlltool: bool,
@@ -220,6 +226,7 @@ impl CachedNeedsConditions {
220226
sanitizer_hwaddress: util::HWASAN_SUPPORTED_TARGETS.contains(target),
221227
sanitizer_memtag: util::MEMTAG_SUPPORTED_TARGETS.contains(target),
222228
sanitizer_shadow_call_stack: util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(target),
229+
sanitizer_safestack: util::SAFESTACK_SUPPORTED_TARGETS.contains(target),
223230
xray: util::XRAY_SUPPORTED_TARGETS.contains(target),
224231

225232
// For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find

src/tools/compiletest/src/util.rs

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[
104104
"x86_64-unknown-openbsd",
105105
];
106106

107+
pub const SAFESTACK_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
108+
107109
pub fn make_new_path(path: &str) -> String {
108110
assert!(cfg!(windows));
109111
// Windows just uses PATH as the library search path, so we have to
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This tests that the safestack attribute is applied when enabling the safe-stack sanitizer.
2+
//
3+
// needs-sanitizer-safestack
4+
// compile-flags: -Zsanitizer=safestack
5+
6+
#![crate_type = "lib"]
7+
8+
// CHECK: ; Function Attrs:{{.*}}safestack
9+
pub fn tagged() {}
10+
11+
// CHECK: attributes #0 = {{.*}}safestack

0 commit comments

Comments
 (0)