Update exploit mitigations documentation
Updates the rustc book with most up to date information about exploit mitigations supported by the Rust compiler.
This commit is contained in:
parent
40a83be6eb
commit
7c385f5a03
4 changed files with 66 additions and 64 deletions
|
|
@ -43,7 +43,8 @@ understood within a given context.
|
|||
This section documents the exploit mitigations applicable to the Rust compiler
|
||||
when building programs for the Linux operating system on the AMD64 architecture
|
||||
and equivalent.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1"
|
||||
class="footnote">1</a></sup>
|
||||
class="footnote">1</a></sup> All examples in this section were built using
|
||||
nightly builds of the Rust compiler on Debian testing.
|
||||
|
||||
The Rust Programming Language currently has no specification. The Rust compiler
|
||||
(i.e., rustc) is the language reference implementation. All references to “the
|
||||
|
|
@ -102,7 +103,10 @@ and unsigned integer computations that cannot be represented in their type,
|
|||
resulting in an overflow or wraparound.
|
||||
|
||||
The Rust compiler supports integer overflow checks, and enables it when debug
|
||||
assertions are enabled since version 1.1.0 (2015-06-25)[14]–[20].
|
||||
assertions are enabled since version 1.0.0 (2015-05-15)[14]–[17], but support
|
||||
for it was not completed until version 1.1.0 (2015-06-25)[16]. An option to
|
||||
control integer overflow checks was later stabilized in version 1.17.0
|
||||
(2017-04-27)[18]–[20].
|
||||
|
||||
```compile_fail
|
||||
fn main() {
|
||||
|
|
@ -120,7 +124,7 @@ $ cargo run
|
|||
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:23
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
|
||||
```
|
||||
Fig. 3. Build and execution of hello-rust-integer with debug assertions
|
||||
Fig. 3. Build and execution of hello-rust-integer with debug assertions
|
||||
enabled.
|
||||
|
||||
```text
|
||||
|
|
@ -130,7 +134,7 @@ $ cargo run --release
|
|||
Running `target/release/hello-rust-integer`
|
||||
u: 0
|
||||
```
|
||||
Fig. 4. Build and execution of hello-rust-integer with debug assertions
|
||||
Fig. 4. Build and execution of hello-rust-integer with debug assertions
|
||||
disabled.
|
||||
|
||||
Integer overflow checks are enabled when debug assertions are enabled (see Fig.
|
||||
|
|
@ -156,7 +160,7 @@ Non-executable memory regions increase the difficulty of exploitation by
|
|||
limiting the memory regions that can be used to execute arbitrary code. Most
|
||||
modern processors provide support for the operating system to mark memory
|
||||
regions as non executable, but it was previously emulated by software, such as
|
||||
in grsecurity/PaX's [PAGEEXEC](https://pax.grsecurity.net/docs/pageexec.txt)
|
||||
in grsecurity/PaX’s [PAGEEXEC](https://pax.grsecurity.net/docs/pageexec.txt)
|
||||
and [SEGMEXEC](https://pax.grsecurity.net/docs/segmexec.txt), on processors
|
||||
that did not provide support for it. This is also known as “No Execute (NX)
|
||||
Bit”, “Execute Disable (XD) Bit”, “Execute Never (XN) Bit”, and others.
|
||||
|
|
@ -171,7 +175,7 @@ $ readelf -l target/release/hello-rust | grep -A 1 GNU_STACK
|
|||
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
|
||||
0x0000000000000000 0x0000000000000000 RW 0x10
|
||||
```
|
||||
Fig. 5. Checking if non-executable memory regions are enabled for a given
|
||||
Fig. 5. Checking if non-executable memory regions are enabled for a given
|
||||
binary.
|
||||
|
||||
The presence of an element of type `PT_GNU_STACK` in the program header table
|
||||
|
|
@ -199,30 +203,33 @@ when attempting to read from the guard page/region. This is also referred to as
|
|||
The Rust compiler supports stack clashing protection via stack probing, and
|
||||
enables it by default since version 1.20.0 (2017-08-31)[26]–[29].
|
||||
|
||||

|
||||
Fig. 6. IDA Pro listing cross references to `__rust_probestack` in hello-rust.
|
||||
|
||||
```rust
|
||||
fn hello() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _: [u64; 1024] = [0; 1024];
|
||||
hello();
|
||||
let v: [u8; 16384] = [1; 16384];
|
||||
let first = &v[0];
|
||||
println!("The first element is: {first}");
|
||||
}
|
||||
```
|
||||
Fig 7. Modified hello-rust.
|
||||
Fig. 6. hello-rust-stack-probe-1 program.
|
||||
|
||||

|
||||
Fig. 8. IDA Pro listing cross references to `__rust_probestack` in modified
|
||||
hello-rust.
|
||||

|
||||
Fig. 7. The "unrolled loop" stack probe variant in modified hello-rust.
|
||||
|
||||
To check if stack clashing protection is enabled for a given binary, search for
|
||||
cross references to `__rust_probestack`. The `__rust_probestack` is called in
|
||||
the prologue of functions whose stack size is larger than a page size (see Fig.
|
||||
6), and can be forced for illustration purposes by modifying the hello-rust
|
||||
example as seen in Fig. 7 and Fig. 8.
|
||||
```rust
|
||||
fn main() {
|
||||
let v: [u8; 65536] = [1; 65536];
|
||||
let first = &v[0];
|
||||
println!("The first element is: {first}");
|
||||
}
|
||||
```
|
||||
Fig. 8. hello-rust-stack-probe-2 program.
|
||||
|
||||

|
||||
Fig. 9. The "standard loop" stack probe variant in modified hello-rust.
|
||||
|
||||
To check if stack clashing protection is enabled for a given binary, look for
|
||||
any of the two stack probe variants in the prologue of functions whose stack
|
||||
size is larger than a page size (see Figs. 6–9).
|
||||
|
||||
|
||||
### Read-only relocations and immediate binding
|
||||
|
|
@ -272,7 +279,7 @@ section indicates immediate binding is not enabled for a given binary.
|
|||
The presence of both an element of type `PT_GNU_RELRO` in the program header
|
||||
table and of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag
|
||||
in the dynamic section indicates full RELRO is enabled for a given binary (see
|
||||
Fig. 9 and Fig. 10).
|
||||
Figs. 9–10).
|
||||
|
||||
<small id="fn:4">4\. And the `DF_1_NOW` flag for some link editors. <a
|
||||
href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></small>
|
||||
|
|
@ -321,7 +328,7 @@ $ cargo run
|
|||
free(): invalid next size (normal)
|
||||
Aborted
|
||||
```
|
||||
Fig. 12. Build and execution of hello-rust-heap with debug assertions enabled.
|
||||
Fig. 12. Build and execution of hello-rust-heap with debug assertions enabled.
|
||||
|
||||
```text
|
||||
$ cargo run --release
|
||||
|
|
@ -331,10 +338,10 @@ $ cargo run --release
|
|||
free(): invalid next size (normal)
|
||||
Aborted
|
||||
```
|
||||
Fig. 13. Build and execution of hello-rust-heap with debug assertions disabled.
|
||||
Fig. 13. Build and execution of hello-rust-heap with debug assertions disabled.
|
||||
|
||||
Heap corruption checks are being performed when using the default allocator
|
||||
(i.e., the GNU Allocator) as seen in Fig. 12 and Fig. 13.
|
||||
Heap corruption checks are performed when using the default allocator (i.e.,
|
||||
the GNU Allocator) (see Figs. 12–13).
|
||||
|
||||
<small id="fn:5">5\. Linux's standard C library default allocator is the GNU
|
||||
Allocator, which is derived from ptmalloc (pthreads malloc) by Wolfram Gloger,
|
||||
|
|
@ -350,15 +357,13 @@ instruction pointer, and checking if this value has changed when returning from
|
|||
a function. This is also known as “Stack Protector” or “Stack Smashing
|
||||
Protector (SSP)”.
|
||||
|
||||
The Rust compiler supports stack smashing protection on nightly builds[42].
|
||||
The Rust compiler supports stack smashing protection on nightly builds[40].
|
||||
|
||||

|
||||
Fig. 14. IDA Pro listing cross references to `__stack_chk_fail` in hello-rust.
|
||||
|
||||
To check if stack smashing protection is enabled for a given binary, search for
|
||||
cross references to `__stack_chk_fail`. The presence of these cross-references
|
||||
in Rust-compiled code (e.g., `hello_rust::main`) indicates that the stack
|
||||
smashing protection is enabled (see Fig. 14).
|
||||
cross references to `__stack_chk_fail` (see Fig. 14).
|
||||
|
||||
|
||||
### Forward-edge control flow protection
|
||||
|
|
@ -380,17 +385,14 @@ commercially available [grsecurity/PaX Reuse Attack Protector
|
|||
(RAP)](https://grsecurity.net/rap_faq).
|
||||
|
||||
The Rust compiler supports forward-edge control flow protection on nightly
|
||||
builds[40]-[41] <sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
|
||||
builds[41]-[42] <sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
|
||||
class="footnote">6</a></sup>.
|
||||
|
||||
```text
|
||||
$ readelf -s -W target/debug/rust-cfi | grep "\.cfi"
|
||||
12: 0000000000005170 46 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_one.cfi
|
||||
15: 00000000000051a0 16 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_two.cfi
|
||||
17: 0000000000005270 396 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi4main.cfi
|
||||
...
|
||||
$ readelf -s -W target/release/hello-rust | grep "\.cfi"
|
||||
5: 0000000000006480 657 FUNC LOCAL DEFAULT 15 _ZN10hello_rust4main17h4e359f1dcd627c83E.cfi
|
||||
```
|
||||
Fig. 15. Checking if LLVM CFI is enabled for a given binary[41].
|
||||
Fig. 15. Checking if LLVM CFI is enabled for a given binary.
|
||||
|
||||
The presence of symbols suffixed with ".cfi" or the `__cfi_init` symbol (and
|
||||
references to `__cfi_check`) indicates that LLVM CFI (i.e., forward-edge
|
||||
|
|
@ -429,21 +431,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 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].
|
||||
The Rust compiler supports shadow stack for the AArch64 architecture<sup
|
||||
id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup>on
|
||||
nightly builds[43]-[44], and also supports safe stack on nightly
|
||||
builds[45]-[46].
|
||||
|
||||
```text
|
||||
$ readelf -s target/release/hello-rust | grep __safestack_init
|
||||
1177: 00000000000057b0 444 FUNC GLOBAL DEFAULT 9 __safestack_init
|
||||
678: 0000000000008c80 426 FUNC GLOBAL DEFAULT 15 __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 (see Fig. 16). Conversely, the absence of the
|
||||
`__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a
|
||||
given binary.
|
||||
enabled for a given binary. Conversely, the absence of the `__safestack_init`
|
||||
symbol indicates that LLVM SafeStack is not enabled for a given binary (see
|
||||
Fig. 16).
|
||||
|
||||
<small id="fn:7">7\. The shadow stack implementation for the AMD64 architecture
|
||||
and equivalent in LLVM was removed due to performance and security issues. <a
|
||||
|
|
@ -458,7 +460,7 @@ the `PT_GNU_STACK` program header indicates whether the stack should be
|
|||
executable, and the absence of this header indicates that the stack should be
|
||||
executable. However, the Linux kernel currently sets the `READ_IMPLIES_EXEC`
|
||||
personality upon loading any executable with the `PT_GNU_STACK` program header
|
||||
and the `PF_X `flag set or with the absence of this header, resulting in not
|
||||
and the `PF_X` flag set or with the absence of this header, resulting in not
|
||||
only the stack, but also all readable virtual memory mappings being executable.
|
||||
|
||||
An attempt to fix this [was made in
|
||||
|
|
@ -560,19 +562,19 @@ to `READ_IMPLIES_EXEC`).
|
|||
25. A. Clark. “Explicitly disable stack execution on linux and bsd #30859.”
|
||||
GitHub. <https://github.com/rust-lang/rust/pull/30859>.
|
||||
|
||||
26. “Replace stack overflow checking with stack probes #16012.” GitHub.
|
||||
26. Zoxc. “Replace stack overflow checking with stack probes #16012.” GitHub.
|
||||
<https://github.com/rust-lang/rust/issues/16012>.
|
||||
|
||||
27. B. Striegel. “Extend stack probe support to non-tier-1 platforms, and
|
||||
clarify policy for mitigating LLVM-dependent unsafety #43241.” GitHub.
|
||||
<https://github.com/rust-lang/rust/issues/43241>.
|
||||
|
||||
28. A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
|
||||
27. A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
|
||||
<https://github.com/rust-lang/rust/pull/42816>.
|
||||
|
||||
29. A. Crichton. “Add \_\_rust\_probestack intrinsic #175.” GitHub.
|
||||
28. A. Crichton. “Add \_\_rust\_probestack intrinsic #175.” GitHub.
|
||||
<https://github.com/rust-lang/compiler-builtins/pull/175>.
|
||||
|
||||
29. S. Guelton, S. Ledru, J. Stone. “Bringing Stack Clash Protection to Clang /
|
||||
X86 — the Open Source Way.” The LLVM Project Blog.
|
||||
<https://blog.llvm.org/posts/2021-01-05-stack-clash-protection/>.
|
||||
|
||||
30. B. Anderson. “Consider applying -Wl,-z,relro or -Wl,-z,relro,-z,now by
|
||||
default #29877.” GitHub. <https://github.com/rust-lang/rust/issues/29877>.
|
||||
|
||||
|
|
@ -605,16 +607,16 @@ to `READ_IMPLIES_EXEC`).
|
|||
39. A. Crichton. “Remove the alloc\_jemalloc crate #55238.” GitHub.
|
||||
<https://github.com/rust-lang/rust/pull/55238>.
|
||||
|
||||
40. R. de C Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
|
||||
for Rust #89653.” GitHub. <https://github.com/rust-lang/rust/issues/89653>.
|
||||
|
||||
41. “ControlFlowIntegrity.” The Rust Unstable Book.
|
||||
[https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity](../unstable-book/compiler-flags/sanitizer.html#controlflowintegrity).
|
||||
|
||||
42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.”
|
||||
40. 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.
|
||||
41. R. de C. Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
|
||||
for Rust #89653.” GitHub. <https://github.com/rust-lang/rust/issues/89653>.
|
||||
|
||||
42. “ControlFlowIntegrity.” The Rust Unstable Book.
|
||||
[https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity](../unstable-book/compiler-flags/sanitizer.html#controlflowintegrity).
|
||||
|
||||
43. I. Lozano. “Add support for LLVM ShadowCallStack #98208.” GitHub.
|
||||
<https://github.com/rust-lang/rust/pull/98208>.
|
||||
|
||||
44. “ShadowCallStack.” The Rust Unstable Book.
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 161 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 152 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
Loading…
Add table
Add a link
Reference in a new issue