Update exploit mitigations documentation · rust-lang/rust@7c385f5 (original) (raw)
`@@ -43,7 +43,8 @@ understood within a given context.
`
43
43
`This section documents the exploit mitigations applicable to the Rust compiler
`
44
44
`when building programs for the Linux operating system on the AMD64 architecture
`
45
45
`and equivalent.<a href="#fn:1"
`
46
``
`-
class="footnote">1
`
``
46
`+
class="footnote">1 All examples in this section were built using
`
``
47
`+
nightly builds of the Rust compiler on Debian testing.
`
47
48
``
48
49
`The Rust Programming Language currently has no specification. The Rust compiler
`
49
50
`(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,
`
102
103
`resulting in an overflow or wraparound.
`
103
104
``
104
105
`The 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].
`
106
110
``
107
111
```` ```compile_fail
`108`
`112`
`fn main() {
`
`@@ -120,7 +124,7 @@ $ cargo run
`
`120`
`124`
`thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:23
`
`121`
`125`
`` note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
``
`122`
`126`
```` ```
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
`
124
128
`enabled.
`
125
129
``
126
130
```` ```text
`@@ -130,7 +134,7 @@ $ cargo run --release
`
`130`
`134`
`` Running `target/release/hello-rust-integer`
``
`131`
`135`
`u: 0
`
`132`
`136`
```` ```
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
`
134
138
`disabled.
`
135
139
``
136
140
`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
`
156
160
`limiting the memory regions that can be used to execute arbitrary code. Most
`
157
161
`modern processors provide support for the operating system to mark memory
`
158
162
`regions as non executable, but it was previously emulated by software, such as
`
159
``
`-
in grsecurity/PaX's PAGEEXEC
`
``
163
`+
in grsecurity/PaX’s PAGEEXEC
`
160
164
`and SEGMEXEC, on processors
`
161
165
`that did not provide support for it. This is also known as “No Execute (NX)
`
162
166
`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
`
171
175
` GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
`
172
176
` 0x0000000000000000 0x0000000000000000 RW 0x10
`
173
177
```` ```
`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
`
`175`
`179`
`binary.
`
`176`
`180`
``
`177`
`181`
`` 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
`
`199`
`203`
`The Rust compiler supports stack clashing protection via stack probing, and
`
`200`
`204`
`enables it by default since version 1.20.0 (2017-08-31)[26]–[29].
`
`201`
`205`
``
`202`
``
`-

`
`203`
``
`` -
Fig. 6. IDA Pro listing cross references to `__rust_probestack` in hello-rust.
``
`204`
``
`-`
`205`
`206`
```` ```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}");
`
208
211
`}
`
``
212
```
``
213
`+
Fig. 6. hello-rust-stack-probe-1 program.
`
209
214
``
``
215
`+
`
``
216
`+
Fig. 7. The "unrolled loop" stack probe variant in modified hello-rust.
`
``
217
+
``
218
```rust
210
219
`fn 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}");
`
213
223
`}
`
214
224
```` ```
`215`
``
`-
Fig 7. Modified hello-rust.
`
``
`225`
`+
Fig. 8. hello-rust-stack-probe-2 program.
`
`216`
`226`
``
`217`
``
`-

`
`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.
`
`220`
`229`
``
`221`
``
`-
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).
`
`226`
`233`
``
`227`
`234`
``
`228`
`235`
`### Read-only relocations and immediate binding
`
`@@ -272,7 +279,7 @@ section indicates immediate binding is not enabled for a given binary.
`
`272`
`279`
`` The presence of both an element of type `PT_GNU_RELRO` in the program header
``
`273`
`280`
`` table and of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag
``
`274`
`281`
`in the dynamic section indicates full RELRO is enabled for a given binary (see
`
`275`
``
`-
Fig. 9 and Fig. 10).
`
``
`282`
`+
Figs. 9–10).
`
`276`
`283`
``
`277`
`284`
`` <small id="fn:4">4\. And the `DF_1_NOW` flag for some link editors. <a
``
`278`
`285`
`href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></small>
`
`@@ -321,7 +328,7 @@ $ cargo run
`
`321`
`328`
`free(): invalid next size (normal)
`
`322`
`329`
`Aborted
`
`323`
`330`
```` ```
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.
`
325
332
``
326
333
```` ```text
`327`
`334`
`$ cargo run --release
`
`@@ -331,10 +338,10 @@ $ cargo run --release
`
`331`
`338`
`free(): invalid next size (normal)
`
`332`
`339`
`Aborted
`
`333`
`340`
```` ```
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.
`
335
342
``
336
``
`-
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).
`
338
345
``
339
346
`5. Linux's standard C library default allocator is the GNU
`
340
347
`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
`
350
357
`a function. This is also known as “Stack Protector” or “Stack Smashing
`
351
358
`Protector (SSP)”.
`
352
359
``
353
``
`-
The Rust compiler supports stack smashing protection on nightly builds[42].
`
``
360
`+
The Rust compiler supports stack smashing protection on nightly builds[40].
`
354
361
``
355
362
`
`
356
363
`` Fig. 14. IDA Pro listing cross references to __stack_chk_fail
in hello-rust.
``
357
364
``
358
365
`To 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).
``
362
367
``
363
368
``
364
369
`### Forward-edge control flow protection
`
`@@ -380,17 +385,14 @@ commercially available [grsecurity/PaX Reuse Attack Protector
`
380
385
`(RAP)](https://grsecurity.net/rap_faq).
`
381
386
``
382
387
`The Rust compiler supports forward-edge control flow protection on nightly
`
383
``
`-
builds[40]-[41] <a href="#fn:6"
`
``
388
`+
builds[41]-[42] <a href="#fn:6"
`
384
389
`class="footnote">6.
`
385
390
``
386
391
```` ```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
`
`392`
`394`
```` ```
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.
`
394
396
``
395
397
`` The presence of symbols suffixed with ".cfi" or the __cfi_init
symbol (and
``
396
398
`` 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
`
429
431
`protection, such as ARM Pointer Authentication, and Intel Shadow Stack as part
`
430
432
`of Intel CET.
`
431
433
``
432
``
`-
The Rust compiler supports shadow stack for aarch64 only <sup id="fnref:7"
`
433
``
`-
role="doc-noteref">7 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">7on
`
``
436
`+
nightly builds[43]-[44], and also supports safe stack on nightly
`
``
437
`+
builds[45]-[46].
`
436
438
``
437
439
```` ```text
`438`
`440`
`$ 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
`
`440`
`442`
```` ```
441
443
`Fig. 16. Checking if LLVM SafeStack is enabled for a given binary.
`
442
444
``
443
445
`` The 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).
`
447
449
``
448
450
`7. The shadow stack implementation for the AMD64 architecture
`
449
451
`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
``
458
460
`executable, and the absence of this header indicates that the stack should be
`
459
461
`` executable. However, the Linux kernel currently sets the READ_IMPLIES_EXEC
``
460
462
`` personality 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
``
462
464
`only the stack, but also all readable virtual memory mappings being executable.
`
463
465
``
464
466
`An attempt to fix this [was made in
`
`` @@ -560,19 +562,19 @@ to READ_IMPLIES_EXEC
).
``
560
562
`25. A. Clark. “Explicitly disable stack execution on linux and bsd #30859.”
`
561
563
` GitHub. https://github.com/rust-lang/rust/pull/30859.
`
562
564
``
563
``
`-
- “Replace stack overflow checking with stack probes #16012.” GitHub.
`
``
565
`+
- Zoxc. “Replace stack overflow checking with stack probes #16012.” GitHub.
`
564
566
`https://github.com/rust-lang/rust/issues/16012.
`
565
567
``
566
``
`-
- 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
``
`-
- A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
`
``
568
`+
- A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
`
571
569
`https://github.com/rust-lang/rust/pull/42816.
`
572
570
``
573
``
`-
- A. Crichton. “Add __rust_probestack intrinsic #175.” GitHub.
`
``
571
`+
- A. Crichton. “Add __rust_probestack intrinsic #175.” GitHub.
`
574
572
`https://github.com/rust-lang/compiler-builtins/pull/175.
`
575
573
``
``
574
`+
- 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
+
576
578
`30. B. Anderson. “Consider applying -Wl,-z,relro or -Wl,-z,relro,-z,now by
`
577
579
` default #29877.” GitHub. https://github.com/rust-lang/rust/issues/29877.
`
578
580
``
`` @@ -605,16 +607,16 @@ to READ_IMPLIES_EXEC
).
``
605
607
`39. A. Crichton. “Remove the alloc_jemalloc crate #55238.” GitHub.
`
606
608
`https://github.com/rust-lang/rust/pull/55238.
`
607
609
``
608
``
`-
- R. de C Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
`
``
610
`+
- bbjornse. “Add codegen option for using LLVM stack smash protection #84197.”
`
``
611
`+
GitHub. https://github.com/rust-lang/rust/pull/84197
`
``
612
+
``
613
`+
- R. de C. Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
`
609
614
` for Rust #89653.” GitHub. https://github.com/rust-lang/rust/issues/89653.
`
610
615
``
611
``
`-
- “ControlFlowIntegrity.” The Rust Unstable Book.
`
``
616
`+
- “ControlFlowIntegrity.” The Rust Unstable Book.
`
612
617
`https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity.
`
613
618
``
614
``
`-
- bbjornse. “add codegen option for using LLVM stack smash protection #84197.”
`
615
``
`-
GitHub. https://github.com/rust-lang/rust/pull/84197
`
616
``
-
617
``
`-
- ivanloz. “Add support for LLVM ShadowCallStack. #98208.” GitHub.
`
``
619
`+
- I. Lozano. “Add support for LLVM ShadowCallStack #98208.” GitHub.
`
618
620
`https://github.com/rust-lang/rust/pull/98208.
`
619
621
``
620
622
`44. “ShadowCallStack.” The Rust Unstable Book.
`