1
Fork 0

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
This commit is contained in:
Wesley Wiser 2023-05-19 19:30:15 -04:00
parent d22314e0f5
commit 019d75b44e
14 changed files with 71 additions and 10 deletions

View file

@ -88,6 +88,9 @@ pub fn sanitize_attrs<'ll>(
attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx)); attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
} }
if enabled.contains(SanitizerSet::SAFESTACK) {
attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
}
attrs attrs
} }

View file

@ -196,6 +196,7 @@ pub enum AttributeKind {
AllocSize = 37, AllocSize = 37,
AllocatedPointer = 38, AllocatedPointer = 38,
AllocAlign = 39, AllocAlign = 39,
SanitizeSafeStack = 40,
} }
/// LLVMIntPredicate /// LLVMIntPredicate

View file

@ -1188,6 +1188,9 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d
if sanitizer.contains(SanitizerSet::HWADDRESS) { if sanitizer.contains(SanitizerSet::HWADDRESS) {
link_sanitizer_runtime(sess, linker, "hwasan"); link_sanitizer_runtime(sess, linker, "hwasan");
} }
if sanitizer.contains(SanitizerSet::SAFESTACK) {
link_sanitizer_runtime(sess, linker, "safestack");
}
} }
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {

View file

@ -96,6 +96,7 @@ enum LLVMRustAttribute {
AllocatedPointer = 38, AllocatedPointer = 38,
AllocAlign = 39, AllocAlign = 39,
#endif #endif
SanitizeSafeStack = 40,
}; };
typedef struct OpaqueRustString *RustStringRef; typedef struct OpaqueRustString *RustStringRef;

View file

@ -234,6 +234,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
case AllocAlign: case AllocAlign:
return Attribute::AllocAlign; return Attribute::AllocAlign;
#endif #endif
case SanitizeSafeStack:
return Attribute::SafeStack;
} }
report_fatal_error("bad AttributeKind"); report_fatal_error("bad AttributeKind");
} }

View file

@ -372,7 +372,7 @@ mod desc {
pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
pub const parse_oom_strategy: &str = "either `panic` or `abort`"; 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_relro_level: &str = "one of: `full`, `partial`, or `off`";
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`"; 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`";
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
pub const parse_cfguard: &str = pub const parse_cfguard: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@ -694,6 +694,7 @@ mod parse {
"shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK, "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
"thread" => SanitizerSet::THREAD, "thread" => SanitizerSet::THREAD,
"hwaddress" => SanitizerSet::HWADDRESS, "hwaddress" => SanitizerSet::HWADDRESS,
"safestack" => SanitizerSet::SAFESTACK,
_ => return false, _ => return false,
} }
} }

View file

@ -815,6 +815,7 @@ bitflags::bitflags! {
const SHADOWCALLSTACK = 1 << 7; const SHADOWCALLSTACK = 1 << 7;
const KCFI = 1 << 8; const KCFI = 1 << 8;
const KERNELADDRESS = 1 << 9; const KERNELADDRESS = 1 << 9;
const SAFESTACK = 1 << 10;
} }
} }
@ -831,6 +832,7 @@ impl SanitizerSet {
SanitizerSet::LEAK => "leak", SanitizerSet::LEAK => "leak",
SanitizerSet::MEMORY => "memory", SanitizerSet::MEMORY => "memory",
SanitizerSet::MEMTAG => "memtag", SanitizerSet::MEMTAG => "memtag",
SanitizerSet::SAFESTACK => "safestack",
SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack", SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
SanitizerSet::THREAD => "thread", SanitizerSet::THREAD => "thread",
SanitizerSet::HWADDRESS => "hwaddress", SanitizerSet::HWADDRESS => "hwaddress",
@ -871,6 +873,7 @@ impl IntoIterator for SanitizerSet {
SanitizerSet::THREAD, SanitizerSet::THREAD,
SanitizerSet::HWADDRESS, SanitizerSet::HWADDRESS,
SanitizerSet::KERNELADDRESS, SanitizerSet::KERNELADDRESS,
SanitizerSet::SAFESTACK,
] ]
.iter() .iter()
.copied() .copied()
@ -2364,6 +2367,7 @@ impl Target {
Some("leak") => SanitizerSet::LEAK, Some("leak") => SanitizerSet::LEAK,
Some("memory") => SanitizerSet::MEMORY, Some("memory") => SanitizerSet::MEMORY,
Some("memtag") => SanitizerSet::MEMTAG, Some("memtag") => SanitizerSet::MEMTAG,
Some("safestack") => SanitizerSet::SAFESTACK,
Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK, Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
Some("thread") => SanitizerSet::THREAD, Some("thread") => SanitizerSet::THREAD,
Some("hwaddress") => SanitizerSet::HWADDRESS, Some("hwaddress") => SanitizerSet::HWADDRESS,

View file

@ -11,6 +11,7 @@ pub fn target() -> Target {
| SanitizerSet::CFI | SanitizerSet::CFI
| SanitizerSet::LEAK | SanitizerSet::LEAK
| SanitizerSet::MEMORY | SanitizerSet::MEMORY
| SanitizerSet::SAFESTACK
| SanitizerSet::THREAD; | SanitizerSet::THREAD;
base.supports_xray = true; base.supports_xray = true;

View file

@ -1017,7 +1017,7 @@ fn supported_sanitizers(
"x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]), "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
"x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]), "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
"x86_64-unknown-linux-gnu" => { "x86_64-unknown-linux-gnu" => {
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) common_libs("linux", "x86_64", &["asan", "lsan", "msan", "safestack", "tsan"])
} }
"x86_64-unknown-linux-musl" => { "x86_64-unknown-linux-musl" => {
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])

View file

@ -66,7 +66,7 @@ equivalent.
| Heap corruption protection | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) | | Heap corruption protection | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
| Stack smashing protection | Yes | Nightly | | Stack smashing protection | Yes | Nightly |
| Forward-edge control flow protection | Yes | Nightly | | Forward-edge control flow protection | Yes | Nightly |
| Backward-edge control flow protection (e.g., shadow and safe stack) | No | | | Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | Nightly |
<small id="fn:1">1\. See <small id="fn:1">1\. See
<https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec> <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
protection, such as ARM Pointer Authentication, and Intel Shadow Stack as protection, such as ARM Pointer Authentication, and Intel Shadow Stack as
part of Intel CET. part of Intel CET.
The Rust compiler does not support shadow or safe stack. There is work The Rust compiler supports shadow stack for aarch64 only
currently ongoing to add support for the sanitizers[40], which may or may <sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup>
not include support for safe stack<sup id="fnref:7" role="doc-noteref"><a on nightly Rust compilers [43]-[44]. Safe stack is available on nightly
href="#fn:7" class="footnote">7</a></sup>. Rust compilers [45]-[46].
```text ```text
$ readelf -s target/release/hello-rust | grep __safestack_init $ readelf -s target/release/hello-rust | grep __safestack_init
1177: 00000000000057b0 444 FUNC GLOBAL DEFAULT 9 __safestack_init
``` ```
Fig. 16.Checking if LLVM SafeStack is enabled for a given binary. Fig. 16.Checking if LLVM SafeStack is enabled for a given binary.
The presence of the `__safestack_init` symbol indicates that LLVM SafeStack The presence of the `__safestack_init` symbol indicates that LLVM SafeStack
is enabled for a given binary. Conversely, the absence of the is enabled for a given binary (see Fig. 16). Conversely, the absence of the
`__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a `__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a
given binary (see Fig. 16). given binary.
<small id="fn:7">7\. The shadow stack implementation for the AMD64 <small id="fn:7">7\. The shadow stack implementation for the AMD64
architecture and equivalent in LLVM was removed due to performance and architecture and equivalent in LLVM was removed due to performance and
@ -628,3 +629,15 @@ defaults (unrelated to `READ_IMPLIES_EXEC`).
42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.” 42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.”
GitHub. <https://github.com/rust-lang/rust/pull/84197> GitHub. <https://github.com/rust-lang/rust/pull/84197>
43. ivanloz. “Add support for LLVM ShadowCallStack. #98208.” GitHub.
<https://github.com/rust-lang/rust/pull/98208>.
44. “ShadowCallStack.” The Rust Unstable Book.
[https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#shadowcallstack](../unstable-book/compiler-flags/sanitizer.html#shadowcallstack).
45. W. Wiser. “Add support for LLVM SafeStack #112000” GitHub.
<https://github.com/rust-lang/rust/pull/112000>
46. “SafeStack.” The Rust Unstable Book.
[https://doc.rust-lang/org/unstable-book/compiler-flags/sanitizer.html#safestack](../unstable-book/compiler-flags/sanitizer.html#safestack).

View file

@ -21,7 +21,8 @@ This feature allows for use of one of following sanitizers:
* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads. * [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
* [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on * [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on
Armv8.5-A Memory Tagging Extension. Armv8.5-A Memory Tagging Extension.
* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow protection. * [SafeStack](#safestack) provides backward-edge control flow protection by separating the stack into safe and unsafe regions.
* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow protection (aarch64 only).
* [ThreadSanitizer](#threadsanitizer) a fast data race detector. * [ThreadSanitizer](#threadsanitizer) a fast data race detector.
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`, 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"`.
See the [LLVM MemTagSanitizer documentation][llvm-memtag] for more details. See the [LLVM MemTagSanitizer documentation][llvm-memtag] for more details.
# SafeStack
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).
SafeStack can be enabled with the `-Zsanitizer=safestack` option and is supported on the following targets:
* `x86_64-unknown-linux-gnu`
See the [Clang SafeStack documentation][clang-safestack] for more details.
# ShadowCallStack # 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 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
[clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi [clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
[clang-safestack]: https://clang.llvm.org/docs/SafeStack.html
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html [clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
[linux-kasan]: https://www.kernel.org/doc/html/latest/dev-tools/kasan.html [linux-kasan]: https://www.kernel.org/doc/html/latest/dev-tools/kasan.html

View file

@ -70,6 +70,11 @@ pub(super) fn handle_needs(
condition: cache.sanitizer_shadow_call_stack, condition: cache.sanitizer_shadow_call_stack,
ignore_reason: "ignored on targets without shadow call stacks", ignore_reason: "ignored on targets without shadow call stacks",
}, },
Need {
name: "needs-sanitizer-safestack",
condition: cache.sanitizer_safestack,
ignore_reason: "ignored on targets without SafeStack support",
},
Need { Need {
name: "needs-run-enabled", name: "needs-run-enabled",
condition: config.run_enabled(), condition: config.run_enabled(),
@ -184,6 +189,7 @@ pub(super) struct CachedNeedsConditions {
sanitizer_hwaddress: bool, sanitizer_hwaddress: bool,
sanitizer_memtag: bool, sanitizer_memtag: bool,
sanitizer_shadow_call_stack: bool, sanitizer_shadow_call_stack: bool,
sanitizer_safestack: bool,
xray: bool, xray: bool,
rust_lld: bool, rust_lld: bool,
i686_dlltool: bool, i686_dlltool: bool,
@ -220,6 +226,7 @@ impl CachedNeedsConditions {
sanitizer_hwaddress: util::HWASAN_SUPPORTED_TARGETS.contains(target), sanitizer_hwaddress: util::HWASAN_SUPPORTED_TARGETS.contains(target),
sanitizer_memtag: util::MEMTAG_SUPPORTED_TARGETS.contains(target), sanitizer_memtag: util::MEMTAG_SUPPORTED_TARGETS.contains(target),
sanitizer_shadow_call_stack: util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(target), sanitizer_shadow_call_stack: util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(target),
sanitizer_safestack: util::SAFESTACK_SUPPORTED_TARGETS.contains(target),
xray: util::XRAY_SUPPORTED_TARGETS.contains(target), xray: util::XRAY_SUPPORTED_TARGETS.contains(target),
// For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find

View file

@ -104,6 +104,8 @@ pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[
"x86_64-unknown-openbsd", "x86_64-unknown-openbsd",
]; ];
pub const SAFESTACK_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
pub fn make_new_path(path: &str) -> String { pub fn make_new_path(path: &str) -> String {
assert!(cfg!(windows)); assert!(cfg!(windows));
// Windows just uses PATH as the library search path, so we have to // Windows just uses PATH as the library search path, so we have to

View file

@ -0,0 +1,11 @@
// This tests that the safestack attribute is applied when enabling the safe-stack sanitizer.
//
// needs-sanitizer-safestack
// compile-flags: -Zsanitizer=safestack
#![crate_type = "lib"]
// CHECK: ; Function Attrs:{{.*}}safestack
pub fn tagged() {}
// CHECK: attributes #0 = {{.*}}safestack