Update exploit mitigations documentation · rust-lang/rust@7c385f5

@@ -43,7 +43,8 @@ understood within a given context.

4343

This section documents the exploit mitigations applicable to the Rust compiler

4444

when building programs for the Linux operating system on the AMD64 architecture

4545

and 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.

47484849

The 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,

102103

resulting in an overflow or wraparound.

103104104105

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].

106110107111

```compile_fail

108112

fn main() {

@@ -120,7 +124,7 @@ $ cargo run

120124

thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:23

121125

note: 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

124128

enabled.

125129126130

```text

@@ -130,7 +134,7 @@ $ cargo run --release

130134

Running `target/release/hello-rust-integer`

131135

u: 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

134138

disabled.

135139136140

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

156160

limiting the memory regions that can be used to execute arbitrary code. Most

157161

modern processors provide support for the operating system to mark memory

158162

regions 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/PaXs [PAGEEXEC](https://pax.grsecurity.net/docs/pageexec.txt)

160164

and [SEGMEXEC](https://pax.grsecurity.net/docs/segmexec.txt), on processors

161165

that did not provide support for it. This is also known as “No Execute (NX)

162166

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

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

175179

binary.

176180177181

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

199203

The Rust compiler supports stack clashing protection via stack probing, and

200204

enables it by default since version 1.20.0 (2017-08-31)[26][29].

201205202-

![Screenshot of IDA Pro listing cross references to __rust_probestack in hello-rust.](images/image1.png "Cross references to __rust_probestack in hello-rust.")

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+

![Screenshot of IDA Pro listing the "unrolled loop" stack probe variant in modified hello-rust.](images/image1.png "The \"unrolled loop\" stack probe variant in modified hello-rust.")

216+

Fig. 7. The "unrolled loop" stack probe variant in modified hello-rust.

217+218+

```rust

210219

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}");

213223

}

214224

```

215-

Fig 7. Modified hello-rust.

225+

Fig. 8. hello-rust-stack-probe-2 program.

216226217-

![Screenshot of IDA Pro listing cross references to __rust_probestack in modified hello-rust.](images/image2.png "Cross references to __rust_probestack in modified hello-rust.")

218-

Fig. 8. IDA Pro listing cross references to `__rust_probestack` in modified

219-

hello-rust.

227+

![Screenshot of IDA Pro listing the "standard loop" stack probe variant in modified hello-rust.](images/image2.png "The \"standard loop\" stack probe variant in modified hello-rust.")

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.

272279

The presence of both an element of type `PT_GNU_RELRO` in the program header

273280

table and of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag

274281

in the dynamic section indicates full RELRO is enabled for a given binary (see

275-

Fig. 9 and Fig. 10).

282+

Figs. 910).

276283277284

<small id="fn:4">4\. And the `DF_1_NOW` flag for some link editors. <a

278285

href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></small>

@@ -321,7 +328,7 @@ $ cargo run

321328

free(): invalid next size (normal)

322329

Aborted

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

331338

free(): invalid next size (normal)

332339

Aborted

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

340347

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

350357

a function. This is also known as “Stack Protector” or “Stack Smashing

351358

Protector (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

![Screenshot of IDA Pro listing cross references to __stack_chk_fail in hello-rust.](images/image3.png "Cross references to __stack_chk_fail in hello-rust.")

356363

Fig. 14. IDA Pro listing cross references to `__stack_chk_fail` in hello-rust.

357364358365

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).

362367363368364369

### Forward-edge control flow protection

@@ -380,17 +385,14 @@ commercially available [grsecurity/PaX Reuse Attack Protector

380385

(RAP)](https://grsecurity.net/rap_faq).

381386382387

The 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"

384389

class="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.

394396395397

The presence of symbols suffixed with ".cfi" or the `__cfi_init` symbol (and

396398

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

429431

protection, such as ARM Pointer Authentication, and Intel Shadow Stack as part

430432

of 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

```

441443

Fig. 16. Checking if LLVM SafeStack is enabled for a given binary.

442444443445

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).

447449448450

<small id="fn:7">7\. The shadow stack implementation for the AMD64 architecture

449451

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

458460

executable, and the absence of this header indicates that the stack should be

459461

executable. However, the Linux kernel currently sets the `READ_IMPLIES_EXEC`

460462

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

462464

only the stack, but also all readable virtual memory mappings being executable.

463465464466

An attempt to fix this [was made in

@@ -560,19 +562,19 @@ to `READ_IMPLIES_EXEC`).

560562

25. 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+576578

30. 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`).

605607

39. 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>.

619621620622

44. “ShadowCallStack.” The Rust Unstable Book.