mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
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:
parent
d22314e0f5
commit
019d75b44e
@ -88,6 +88,9 @@ pub fn sanitize_attrs<'ll>(
|
||||
|
||||
attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
|
||||
}
|
||||
if enabled.contains(SanitizerSet::SAFESTACK) {
|
||||
attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
|
||||
}
|
||||
attrs
|
||||
}
|
||||
|
||||
|
@ -196,6 +196,7 @@ pub enum AttributeKind {
|
||||
AllocSize = 37,
|
||||
AllocatedPointer = 38,
|
||||
AllocAlign = 39,
|
||||
SanitizeSafeStack = 40,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
|
@ -1188,6 +1188,9 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d
|
||||
if sanitizer.contains(SanitizerSet::HWADDRESS) {
|
||||
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) {
|
||||
|
@ -96,6 +96,7 @@ enum LLVMRustAttribute {
|
||||
AllocatedPointer = 38,
|
||||
AllocAlign = 39,
|
||||
#endif
|
||||
SanitizeSafeStack = 40,
|
||||
};
|
||||
|
||||
typedef struct OpaqueRustString *RustStringRef;
|
||||
|
@ -234,6 +234,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
|
||||
case AllocAlign:
|
||||
return Attribute::AllocAlign;
|
||||
#endif
|
||||
case SanitizeSafeStack:
|
||||
return Attribute::SafeStack;
|
||||
}
|
||||
report_fatal_error("bad AttributeKind");
|
||||
}
|
||||
|
@ -372,7 +372,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`, `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_cfguard: &str =
|
||||
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
|
||||
@ -694,6 +694,7 @@ mod parse {
|
||||
"shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
|
||||
"thread" => SanitizerSet::THREAD,
|
||||
"hwaddress" => SanitizerSet::HWADDRESS,
|
||||
"safestack" => SanitizerSet::SAFESTACK,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
@ -815,6 +815,7 @@ bitflags::bitflags! {
|
||||
const SHADOWCALLSTACK = 1 << 7;
|
||||
const KCFI = 1 << 8;
|
||||
const KERNELADDRESS = 1 << 9;
|
||||
const SAFESTACK = 1 << 10;
|
||||
}
|
||||
}
|
||||
|
||||
@ -831,6 +832,7 @@ impl SanitizerSet {
|
||||
SanitizerSet::LEAK => "leak",
|
||||
SanitizerSet::MEMORY => "memory",
|
||||
SanitizerSet::MEMTAG => "memtag",
|
||||
SanitizerSet::SAFESTACK => "safestack",
|
||||
SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
|
||||
SanitizerSet::THREAD => "thread",
|
||||
SanitizerSet::HWADDRESS => "hwaddress",
|
||||
@ -871,6 +873,7 @@ impl IntoIterator for SanitizerSet {
|
||||
SanitizerSet::THREAD,
|
||||
SanitizerSet::HWADDRESS,
|
||||
SanitizerSet::KERNELADDRESS,
|
||||
SanitizerSet::SAFESTACK,
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
@ -2364,6 +2367,7 @@ impl Target {
|
||||
Some("leak") => SanitizerSet::LEAK,
|
||||
Some("memory") => SanitizerSet::MEMORY,
|
||||
Some("memtag") => SanitizerSet::MEMTAG,
|
||||
Some("safestack") => SanitizerSet::SAFESTACK,
|
||||
Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
|
||||
Some("thread") => SanitizerSet::THREAD,
|
||||
Some("hwaddress") => SanitizerSet::HWADDRESS,
|
||||
|
@ -11,6 +11,7 @@ pub fn target() -> Target {
|
||||
| SanitizerSet::CFI
|
||||
| SanitizerSet::LEAK
|
||||
| SanitizerSet::MEMORY
|
||||
| SanitizerSet::SAFESTACK
|
||||
| SanitizerSet::THREAD;
|
||||
base.supports_xray = true;
|
||||
|
||||
|
@ -1017,7 +1017,7 @@ fn supported_sanitizers(
|
||||
"x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
|
||||
"x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
|
||||
"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" => {
|
||||
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
|
||||
|
@ -66,7 +66,7 @@ equivalent.
|
||||
| Heap corruption protection | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
|
||||
| Stack smashing 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
|
||||
<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
|
||||
part of Intel CET.
|
||||
|
||||
The Rust compiler does not support shadow or safe stack. There is work
|
||||
currently ongoing to add support for the sanitizers[40], which may or may
|
||||
not include support for safe stack<sup id="fnref:7" role="doc-noteref"><a
|
||||
href="#fn:7" class="footnote">7</a></sup>.
|
||||
The Rust compiler supports shadow stack for aarch64 only
|
||||
<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup>
|
||||
on nightly Rust compilers [43]-[44]. Safe stack is available on nightly
|
||||
Rust compilers [45]-[46].
|
||||
|
||||
```text
|
||||
$ 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.
|
||||
|
||||
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
|
||||
given binary (see Fig. 16).
|
||||
given binary.
|
||||
|
||||
<small id="fn:7">7\. The shadow stack implementation for the AMD64
|
||||
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.”
|
||||
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).
|
||||
|
@ -21,7 +21,8 @@ This feature allows for use of one of following sanitizers:
|
||||
* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
|
||||
* [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
# 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 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-lsan]: https://clang.llvm.org/docs/LeakSanitizer.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-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
|
||||
[linux-kasan]: https://www.kernel.org/doc/html/latest/dev-tools/kasan.html
|
||||
|
@ -70,6 +70,11 @@ pub(super) fn handle_needs(
|
||||
condition: cache.sanitizer_shadow_call_stack,
|
||||
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 {
|
||||
name: "needs-run-enabled",
|
||||
condition: config.run_enabled(),
|
||||
@ -184,6 +189,7 @@ pub(super) struct CachedNeedsConditions {
|
||||
sanitizer_hwaddress: bool,
|
||||
sanitizer_memtag: bool,
|
||||
sanitizer_shadow_call_stack: bool,
|
||||
sanitizer_safestack: bool,
|
||||
xray: bool,
|
||||
rust_lld: bool,
|
||||
i686_dlltool: bool,
|
||||
@ -220,6 +226,7 @@ impl CachedNeedsConditions {
|
||||
sanitizer_hwaddress: util::HWASAN_SUPPORTED_TARGETS.contains(target),
|
||||
sanitizer_memtag: util::MEMTAG_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),
|
||||
|
||||
// For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find
|
||||
|
@ -104,6 +104,8 @@ pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[
|
||||
"x86_64-unknown-openbsd",
|
||||
];
|
||||
|
||||
pub const SAFESTACK_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
|
||||
|
||||
pub fn make_new_path(path: &str) -> String {
|
||||
assert!(cfg!(windows));
|
||||
// Windows just uses PATH as the library search path, so we have to
|
||||
|
11
tests/codegen/sanitizer-safestack-attr-check.rs
Normal file
11
tests/codegen/sanitizer-safestack-attr-check.rs
Normal 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
|
Loading…
Reference in New Issue
Block a user