Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for LLVM SafeStack #112000

Merged
merged 2 commits into from
May 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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