Skip to content

Commit

Permalink
Auto merge of rust-lang#112000 - wesleywiser:safestack, r=Amanieu
Browse files Browse the repository at this point in the history
Add support for LLVM SafeStack

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

cc `@rcvalle` rust-lang#39699
  • Loading branch information
bors committed May 28, 2023
2 parents b9c5fdc + 019d75b commit 2560b80
Show file tree
Hide file tree
Showing 14 changed files with 82 additions and 91 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Expand Up @@ -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
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Expand Up @@ -196,6 +196,7 @@ pub enum AttributeKind {
AllocSize = 37,
AllocatedPointer = 38,
AllocAlign = 39,
SanitizeSafeStack = 40,
}

/// LLVMIntPredicate
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
Expand Up @@ -96,6 +96,7 @@ enum LLVMRustAttribute {
AllocatedPointer = 38,
AllocAlign = 39,
#endif
SanitizeSafeStack = 40,
};

typedef struct OpaqueRustString *RustStringRef;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Expand Up @@ -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");
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_session/src/options.rs
Expand Up @@ -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`";
Expand Down Expand Up @@ -694,6 +694,7 @@ mod parse {
"shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
"thread" => SanitizerSet::THREAD,
"hwaddress" => SanitizerSet::HWADDRESS,
"safestack" => SanitizerSet::SAFESTACK,
_ => return false,
}
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Expand Up @@ -815,6 +815,7 @@ bitflags::bitflags! {
const SHADOWCALLSTACK = 1 << 7;
const KCFI = 1 << 8;
const KERNELADDRESS = 1 << 9;
const SAFESTACK = 1 << 10;
}
}

Expand All @@ -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",
Expand Down Expand Up @@ -871,6 +873,7 @@ impl IntoIterator for SanitizerSet {
SanitizerSet::THREAD,
SanitizerSet::HWADDRESS,
SanitizerSet::KERNELADDRESS,
SanitizerSet::SAFESTACK,
]
.iter()
.copied()
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
Expand Up @@ -11,6 +11,7 @@ pub fn target() -> Target {
| SanitizerSet::CFI
| SanitizerSet::LEAK
| SanitizerSet::MEMORY
| SanitizerSet::SAFESTACK
| SanitizerSet::THREAD;
base.supports_xray = true;

Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/llvm.rs
Expand Up @@ -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"])
Expand Down
119 changes: 31 additions & 88 deletions src/doc/rustc/src/exploit-mitigations.md
Expand Up @@ -55,88 +55,18 @@ Table I \
Summary of exploit mitigations supported by the Rust compiler when building
programs for the Linux operating system on the AMD64 architecture and
equivalent.
<table class="table">
<tr>
<td><strong>Exploit mitigation</strong>
</td>
<td><strong>Supported and enabled by default</strong>
</td>
<td><strong>Since</strong>
</td>
</tr>
<tr>
<td>Position-independent executable
</td>
<td>Yes
</td>
<td>0.12.0 (2014-10-09)
</td>
</tr>
<tr>
<td>Integer overflow checks
</td>
<td>Yes (enabled when debug assertions are enabled, and disabled when debug assertions are disabled)
</td>
<td>1.1.0 (2015-06-25)
</td>
</tr>
<tr>
<td>Non-executable memory regions
</td>
<td>Yes
</td>
<td>1.8.0 (2016-04-14)
</td>
</tr>
<tr>
<td>Stack clashing protection
</td>
<td>Yes
</td>
<td>1.20.0 (2017-08-31)
</td>
</tr>
<tr>
<td>Read-only relocations and immediate binding
</td>
<td>Yes
</td>
<td>1.21.0 (2017-10-12)
</td>
</tr>
<tr>
<td>Heap corruption protection
</td>
<td>Yes
</td>
<td>1.32.0 (2019-01-17) (via operating system default or specified allocator)
</td>
</tr>
<tr>
<td>Stack smashing protection
</td>
<td>Yes
</td>
<td>Nightly
</td>
</tr>
<tr>
<td>Forward-edge control flow protection
</td>
<td>Yes
</td>
<td>Nightly
</td>
</tr>
<tr>
<td>Backward-edge control flow protection (e.g., shadow and safe stack)
</td>
<td>No
</td>
<td>
</td>
</tr>
</table>

| Exploit mitigation | Supported and enabled by default | Since |
| - | - | - |
| Position-independent executable | Yes | 0.12.0 (2014-10-09) |
| Integer overflow checks | Yes (enabled when debug assertions are enabled, and disabled when debug assertions are disabled) | 1.1.0 (2015-06-25) |
| Non-executable memory regions | Yes | 1.8.0 (2016-04-14) |
| Stack clashing protection | Yes | 1.20.0 (2017-08-31) |
| Read-only relocations and immediate binding | Yes | 1.21.0 (2017-10-12) |
| 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) | Yes | Nightly |

<small id="fn:1">1\. See
<https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec>
Expand Down Expand Up @@ -513,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
Expand Down Expand Up @@ -698,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).
14 changes: 13 additions & 1 deletion src/doc/unstable-book/src/compiler-flags/sanitizer.md
Expand Up @@ -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`,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/tools/compiletest/src/header/needs.rs
Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/tools/compiletest/src/util.rs
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions tests/codegen/sanitizer-safestack-attr-check.rs
@@ -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

0 comments on commit 2560b80

Please sign in to comment.