Update exploit mitigations documentation · rust-lang/rust@7c385f5
@@ -43,7 +43,8 @@ understood within a given context.
4343This section documents the exploit mitigations applicable to the Rust compiler
4444when building programs for the Linux operating system on the AMD64 architecture
4545and equivalent.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1"
46-class="footnote">1</a></sup>
46+class="footnote">1</a></sup> All examples in this section were built using
47+nightly builds of the Rust compiler on Debian testing.
47484849The Rust Programming Language currently has no specification. The Rust compiler
4950(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,
102103resulting in an overflow or wraparound.
103104104105The Rust compiler supports integer overflow checks, and enables it when debug
105-assertions are enabled since version 1.1.0 (2015-06-25)[14]–[20].
106+assertions are enabled since version 1.0.0 (2015-05-15)[14]–[17], but support
107+for it was not completed until version 1.1.0 (2015-06-25)[16]. An option to
108+control integer overflow checks was later stabilized in version 1.17.0
109+(2017-04-27)[18]–[20].
106110107111```compile_fail
108112fn main() {
@@ -120,7 +124,7 @@ $ cargo run
120124thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:23
121125note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
122126```
123-Fig. 3. Build and execution of hello-rust-integer with debug assertions
127+Fig. 3. Build and execution of hello-rust-integer with debug assertions
124128enabled.
125129126130```text
@@ -130,7 +134,7 @@ $ cargo run --release
130134 Running `target/release/hello-rust-integer`
131135u: 0
132136```
133-Fig. 4. Build and execution of hello-rust-integer with debug assertions
137+Fig. 4. Build and execution of hello-rust-integer with debug assertions
134138disabled.
135139136140Integer 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
156160limiting the memory regions that can be used to execute arbitrary code. Most
157161modern processors provide support for the operating system to mark memory
158162regions as non executable, but it was previously emulated by software, such as
159-in grsecurity/PaX's [PAGEEXEC](https://pax.grsecurity.net/docs/pageexec.txt)
163+in grsecurity/PaX’s [PAGEEXEC](https://pax.grsecurity.net/docs/pageexec.txt)
160164and [SEGMEXEC](https://pax.grsecurity.net/docs/segmexec.txt), on processors
161165that did not provide support for it. This is also known as “No Execute (NX)
162166Bit”, “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
171175 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
172176 0x0000000000000000 0x0000000000000000 RW 0x10
173177```
174-Fig. 5. Checking if non-executable memory regions are enabled for a given
178+Fig. 5. Checking if non-executable memory regions are enabled for a given
175179binary.
176180177181The 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
199203The Rust compiler supports stack clashing protection via stack probing, and
200204enables it by default since version 1.20.0 (2017-08-31)[26]–[29].
201205202-
203-Fig. 6. IDA Pro listing cross references to `__rust_probestack` in hello-rust.
204-205206```rust
206-fn hello() {
207-println!("Hello, world!");
207+fn main() {
208+let v: [u8; 16384] = [1; 16384];
209+let first = &v[0];
210+println!("The first element is: {first}");
208211}
212+```
213+Fig. 6. hello-rust-stack-probe-1 program.
209214215+
216+Fig. 7. The "unrolled loop" stack probe variant in modified hello-rust.
217+218+```rust
210219fn main() {
211-let _: [u64; 1024] = [0; 1024];
212-hello();
220+let v: [u8; 65536] = [1; 65536];
221+let first = &v[0];
222+println!("The first element is: {first}");
213223}
214224```
215-Fig 7. Modified hello-rust.
225+Fig. 8. hello-rust-stack-probe-2 program.
216226217-
218-Fig. 8. IDA Pro listing cross references to `__rust_probestack` in modified
219-hello-rust.
227+
228+Fig. 9. The "standard loop" stack probe variant in modified hello-rust.
220229221-To check if stack clashing protection is enabled for a given binary, search for
222-cross references to `__rust_probestack`. The `__rust_probestack` is called in
223-the prologue of functions whose stack size is larger than a page size (see Fig.
224-6), and can be forced for illustration purposes by modifying the hello-rust
225-example as seen in Fig. 7 and Fig. 8.
230+To check if stack clashing protection is enabled for a given binary, look for
231+any of the two stack probe variants in the prologue of functions whose stack
232+size is larger than a page size (see Figs. 6–9).
226233227234228235### Read-only relocations and immediate binding
@@ -272,7 +279,7 @@ section indicates immediate binding is not enabled for a given binary.
272279The presence of both an element of type `PT_GNU_RELRO` in the program header
273280table and of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag
274281in the dynamic section indicates full RELRO is enabled for a given binary (see
275-Fig. 9 and Fig. 10).
282+Figs. 9–10).
276283277284<small id="fn:4">4\. And the `DF_1_NOW` flag for some link editors. <a
278285href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></small>
@@ -321,7 +328,7 @@ $ cargo run
321328free(): invalid next size (normal)
322329Aborted
323330```
324-Fig. 12. Build and execution of hello-rust-heap with debug assertions enabled.
331+Fig. 12. Build and execution of hello-rust-heap with debug assertions enabled.
325332326333```text
327334$ cargo run --release
@@ -331,10 +338,10 @@ $ cargo run --release
331338free(): invalid next size (normal)
332339Aborted
333340```
334-Fig. 13. Build and execution of hello-rust-heap with debug assertions disabled.
341+Fig. 13. Build and execution of hello-rust-heap with debug assertions disabled.
335342336-Heap corruption checks are being performed when using the default allocator
337-(i.e., the GNU Allocator) as seen in Fig. 12 and Fig. 13.
343+Heap corruption checks are performed when using the default allocator (i.e.,
344+the GNU Allocator) (see Figs. 12–13).
338345339346<small id="fn:5">5\. Linux's standard C library default allocator is the GNU
340347Allocator, 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
350357a function. This is also known as “Stack Protector” or “Stack Smashing
351358Protector (SSP)”.
352359353-The Rust compiler supports stack smashing protection on nightly builds[42].
360+The Rust compiler supports stack smashing protection on nightly builds[40].
354361355362
356363Fig. 14. IDA Pro listing cross references to `__stack_chk_fail` in hello-rust.
357364358365To check if stack smashing protection is enabled for a given binary, search for
359-cross references to `__stack_chk_fail`. The presence of these cross-references
360-in Rust-compiled code (e.g., `hello_rust::main`) indicates that the stack
361-smashing protection is enabled (see Fig. 14).
366+cross references to `__stack_chk_fail` (see Fig. 14).
362367363368364369### Forward-edge control flow protection
@@ -380,17 +385,14 @@ commercially available [grsecurity/PaX Reuse Attack Protector
380385(RAP)](https://grsecurity.net/rap_faq).
381386382387The Rust compiler supports forward-edge control flow protection on nightly
383-builds[40]-[41] <sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
388+builds[41]-[42] <sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
384389class="footnote">6</a></sup>.
385390386391```text
387-$ readelf -s -W target/debug/rust-cfi | grep "\.cfi"
388- 12: 0000000000005170 46 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_one.cfi
389- 15: 00000000000051a0 16 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_two.cfi
390- 17: 0000000000005270 396 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi4main.cfi
391-...
392+$ readelf -s -W target/release/hello-rust | grep "\.cfi"
393+ 5: 0000000000006480 657 FUNC LOCAL DEFAULT 15 _ZN10hello_rust4main17h4e359f1dcd627c83E.cfi
392394```
393-Fig. 15. Checking if LLVM CFI is enabled for a given binary[41].
395+Fig. 15. Checking if LLVM CFI is enabled for a given binary.
394396395397The presence of symbols suffixed with ".cfi" or the `__cfi_init` symbol (and
396398references 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
429431protection, such as ARM Pointer Authentication, and Intel Shadow Stack as part
430432of Intel CET.
431433432-The Rust compiler supports shadow stack for aarch64 only <sup id="fnref:7"
433-role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup> on nightly Rust
434-compilers [43]-[44]. Safe stack is available on nightly Rust compilers
435-[45]-[46].
434+The Rust compiler supports shadow stack for the AArch64 architecture<sup
435+id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup>on
436+nightly builds[43]-[44], and also supports safe stack on nightly
437+builds[45]-[46].
436438437439```text
438440$ readelf -s target/release/hello-rust | grep __safestack_init
439- 1177: 00000000000057b0 444 FUNC GLOBAL DEFAULT 9 __safestack_init
441+ 678: 0000000000008c80 426 FUNC GLOBAL DEFAULT 15 __safestack_init
440442```
441443Fig. 16. Checking if LLVM SafeStack is enabled for a given binary.
442444443445The presence of the `__safestack_init` symbol indicates that LLVM SafeStack is
444-enabled for a given binary (see Fig. 16). Conversely, the absence of the
445-`__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a
446-given binary.
446+enabled for a given binary. Conversely, the absence of the `__safestack_init`
447+symbol indicates that LLVM SafeStack is not enabled for a given binary (see
448+Fig. 16).
447449448450<small id="fn:7">7\. The shadow stack implementation for the AMD64 architecture
449451and 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
458460executable, and the absence of this header indicates that the stack should be
459461executable. However, the Linux kernel currently sets the `READ_IMPLIES_EXEC`
460462personality upon loading any executable with the `PT_GNU_STACK` program header
461-and the `PF_X `flag set or with the absence of this header, resulting in not
463+and the `PF_X` flag set or with the absence of this header, resulting in not
462464only the stack, but also all readable virtual memory mappings being executable.
463465464466An attempt to fix this [was made in
@@ -560,19 +562,19 @@ to `READ_IMPLIES_EXEC`).
56056225. A. Clark. “Explicitly disable stack execution on linux and bsd #30859.”
561563 GitHub. <https://github.com/rust-lang/rust/pull/30859>.
562564563-26. “Replace stack overflow checking with stack probes #16012.” GitHub.
565+26. Zoxc. “Replace stack overflow checking with stack probes #16012.” GitHub.
564566<https://github.com/rust-lang/rust/issues/16012>.
565567566-27. B. Striegel. “Extend stack probe support to non-tier-1 platforms, and
567- clarify policy for mitigating LLVM-dependent unsafety #43241.” GitHub.
568-<https://github.com/rust-lang/rust/issues/43241>.
569-570-28. A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
568+27. A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
571569<https://github.com/rust-lang/rust/pull/42816>.
572570573-29. A. Crichton. “Add \_\_rust\_probestack intrinsic #175.” GitHub.
571+28. A. Crichton. “Add \_\_rust\_probestack intrinsic #175.” GitHub.
574572<https://github.com/rust-lang/compiler-builtins/pull/175>.
575573574+29. S. Guelton, S. Ledru, J. Stone. “Bringing Stack Clash Protection to Clang /
575+ X86 — the Open Source Way.” The LLVM Project Blog.
576+<https://blog.llvm.org/posts/2021-01-05-stack-clash-protection/>.
577+57657830. B. Anderson. “Consider applying -Wl,-z,relro or -Wl,-z,relro,-z,now by
577579 default #29877.” GitHub. <https://github.com/rust-lang/rust/issues/29877>.
578580@@ -605,16 +607,16 @@ to `READ_IMPLIES_EXEC`).
60560739. A. Crichton. “Remove the alloc\_jemalloc crate #55238.” GitHub.
606608<https://github.com/rust-lang/rust/pull/55238>.
607609608-40. R. de C Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
610+40. bbjornse. “Add codegen option for using LLVM stack smash protection #84197.”
611+ GitHub. <https://github.com/rust-lang/rust/pull/84197>
612+613+41. R. de C. Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
609614 for Rust #89653.” GitHub. <https://github.com/rust-lang/rust/issues/89653>.
610615611-41. “ControlFlowIntegrity.” The Rust Unstable Book.
616+42. “ControlFlowIntegrity.” The Rust Unstable Book.
612617[https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity](../unstable-book/compiler-flags/sanitizer.html#controlflowintegrity).
613618614-42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.”
615- GitHub. <https://github.com/rust-lang/rust/pull/84197>
616-617-43. ivanloz. “Add support for LLVM ShadowCallStack. #98208.” GitHub.
619+43. I. Lozano. “Add support for LLVM ShadowCallStack #98208.” GitHub.
618620<https://github.com/rust-lang/rust/pull/98208>.
61962162062244. “ShadowCallStack.” The Rust Unstable Book.