Skip to content

Commit 2560b80

Browse files
committed
Auto merge of rust-lang#112000 - wesleywiser:safestack, r=Amanieu
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
2 parents b9c5fdc + 019d75b commit 2560b80

File tree

14 files changed

+82
-91
lines changed

14 files changed

+82
-91
lines changed

Diff for: 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

Diff for: 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

Diff for: 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) {

Diff for: 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;

Diff for: 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
}

Diff for: 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
}

Diff for: 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,

Diff for: 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

Diff for: 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"])

Diff for: src/doc/rustc/src/exploit-mitigations.md

+31-88
Original file line numberDiff line numberDiff line change
@@ -55,88 +55,18 @@ Table I \
5555
Summary of exploit mitigations supported by the Rust compiler when building
5656
programs for the Linux operating system on the AMD64 architecture and
5757
equivalent.
58-
<table class="table">
59-
<tr>
60-
<td><strong>Exploit mitigation</strong>
61-
</td>
62-
<td><strong>Supported and enabled by default</strong>
63-
</td>
64-
<td><strong>Since</strong>
65-
</td>
66-
</tr>
67-
<tr>
68-
<td>Position-independent executable
69-
</td>
70-
<td>Yes
71-
</td>
72-
<td>0.12.0 (2014-10-09)
73-
</td>
74-
</tr>
75-
<tr>
76-
<td>Integer overflow checks
77-
</td>
78-
<td>Yes (enabled when debug assertions are enabled, and disabled when debug assertions are disabled)
79-
</td>
80-
<td>1.1.0 (2015-06-25)
81-
</td>
82-
</tr>
83-
<tr>
84-
<td>Non-executable memory regions
85-
</td>
86-
<td>Yes
87-
</td>
88-
<td>1.8.0 (2016-04-14)
89-
</td>
90-
</tr>
91-
<tr>
92-
<td>Stack clashing protection
93-
</td>
94-
<td>Yes
95-
</td>
96-
<td>1.20.0 (2017-08-31)
97-
</td>
98-
</tr>
99-
<tr>
100-
<td>Read-only relocations and immediate binding
101-
</td>
102-
<td>Yes
103-
</td>
104-
<td>1.21.0 (2017-10-12)
105-
</td>
106-
</tr>
107-
<tr>
108-
<td>Heap corruption protection
109-
</td>
110-
<td>Yes
111-
</td>
112-
<td>1.32.0 (2019-01-17) (via operating system default or specified allocator)
113-
</td>
114-
</tr>
115-
<tr>
116-
<td>Stack smashing protection
117-
</td>
118-
<td>Yes
119-
</td>
120-
<td>Nightly
121-
</td>
122-
</tr>
123-
<tr>
124-
<td>Forward-edge control flow protection
125-
</td>
126-
<td>Yes
127-
</td>
128-
<td>Nightly
129-
</td>
130-
</tr>
131-
<tr>
132-
<td>Backward-edge control flow protection (e.g., shadow and safe stack)
133-
</td>
134-
<td>No
135-
</td>
136-
<td>
137-
</td>
138-
</tr>
139-
</table>
58+
59+
| Exploit mitigation | Supported and enabled by default | Since |
60+
| - | - | - |
61+
| Position-independent executable | Yes | 0.12.0 (2014-10-09) |
62+
| Integer overflow checks | Yes (enabled when debug assertions are enabled, and disabled when debug assertions are disabled) | 1.1.0 (2015-06-25) |
63+
| Non-executable memory regions | Yes | 1.8.0 (2016-04-14) |
64+
| Stack clashing protection | Yes | 1.20.0 (2017-08-31) |
65+
| Read-only relocations and immediate binding | Yes | 1.21.0 (2017-10-12) |
66+
| Heap corruption protection | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
67+
| Stack smashing protection | Yes | Nightly |
68+
| Forward-edge control flow protection | Yes | Nightly |
69+
| Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | Nightly |
14070

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

516-
The Rust compiler does not support shadow or safe stack. There is work
517-
currently ongoing to add support for the sanitizers[40], which may or may
518-
not include support for safe stack<sup id="fnref:7" role="doc-noteref"><a
519-
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].
520450

521451
```text
522452
$ readelf -s target/release/hello-rust | grep __safestack_init
453+
1177: 00000000000057b0 444 FUNC GLOBAL DEFAULT 9 __safestack_init
523454
```
524455
Fig. 16. Checking if LLVM SafeStack is enabled for a given binary.
525456

526457
The presence of the `__safestack_init` symbol indicates that LLVM SafeStack
527-
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
528459
`__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a
529-
given binary (see Fig. 16).
460+
given binary.
530461

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

699630
42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.”
700631
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).

Diff for: 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

Diff for: 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

Diff for: 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

Diff for: tests/codegen/sanitizer-safestack-attr-check.rs

+11
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)