Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit c5708ca

Browse files
committed
Add documentation for LLVM CFI support
This commit adds initial documentation for LLVM Control Flow Integrity (CFI) support to the Rust compiler (see rust-lang#89652 and rust-lang#89653).
1 parent 5d30e93 commit c5708ca

File tree

2 files changed

+202
-19
lines changed

2 files changed

+202
-19
lines changed

src/doc/rustc/src/exploit-mitigations.md

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ equivalent.
123123
<tr>
124124
<td>Forward-edge control flow protection
125125
</td>
126-
<td>No
126+
<td>Yes
127127
</td>
128-
<td>
128+
<td>Nightly
129129
</td>
130130
</tr>
131131
<tr>
@@ -465,24 +465,27 @@ implementations such as [LLVM ControlFlowIntegrity
465465
commercially available [grsecurity/PaX Reuse Attack Protector
466466
(RAP)](https://grsecurity.net/rap_faq).
467467

468-
The Rust compiler does not support forward-edge control flow protection on
469-
Linux<sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
470-
class="footnote">6</a></sup>. There is work currently ongoing to add support
471-
for the [sanitizers](https://github.com/google/sanitizers)[40], which may or
472-
may not include support for LLVM CFI.
468+
The Rust compiler supports forward-edge control flow protection on nightly
469+
builds[40]-[41] <sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
470+
class="footnote">6</a></sup>.
473471

474472
```text
475-
$ readelf -s target/release/hello-rust | grep __cfi_init
473+
$ readelf -s -W target/debug/rust-cfi | grep "\.cfi"
474+
12: 0000000000005170 46 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_one.cfi
475+
15: 00000000000051a0 16 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_two.cfi
476+
17: 0000000000005270 396 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi4main.cfi
477+
...
476478
```
477-
Fig. 15. Checking if LLVM CFI is enabled for a given binary.
479+
Fig. 15. Checking if LLVM CFI is enabled for a given binary[41].
478480

479-
The presence of the `__cfi_init` symbol (and references to `__cfi_check`)
480-
indicates that LLVM CFI (i.e., forward-edge control flow protection) is
481-
enabled for a given binary. Conversely, the absence of the `__cfi_init`
482-
symbol (and references to `__cfi_check`) indicates that LLVM CFI is not
483-
enabled for a given binary (see Fig. 15).
481+
The presence of symbols suffixed with ".cfi" or the `__cfi_init` symbol (and
482+
references to `__cfi_check`) indicates that LLVM CFI (i.e., forward-edge control
483+
flow protection) is enabled for a given binary. Conversely, the absence of
484+
symbols suffixed with ".cfi" or the `__cfi_init` symbol (and references to
485+
`__cfi_check`) indicates that LLVM CFI is not enabled for a given binary (see
486+
Fig. 15).
484487

485-
<small id="fn:6">6\. It supports Control Flow Guard (CFG) on Windows (see
488+
<small id="fn:6">6\. It also supports Control Flow Guard (CFG) on Windows (see
486489
<https://github.com/rust-lang/rust/issues/68793>). <a href="#fnref:6"
487490
class="reversefootnote" role="doc-backlink">↩</a></small>
488491

@@ -689,5 +692,8 @@ defaults (unrelated to `READ_IMPLIES_EXEC`).
689692
39. A. Crichton. “Remove the alloc\_jemalloc crate #55238.” GitHub.
690693
<https://github.com/rust-lang/rust/pull/55238>.
691694

692-
40. J. Aparicio. 2017. “Tracking issue for sanitizer support #39699.”
693-
<https://github.com/rust-lang/rust/issues/39699>.
695+
40. R. de C Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
696+
for Rust #89653.” GitHub. <https://github.com/rust-lang/rust/issues/89653>.
697+
698+
41. “ControlFlowIntegrity.” The Rust Unstable Book.
699+
<https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity>.

src/doc/unstable-book/src/compiler-flags/sanitizer.md

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
# `sanitizer`
22

3-
The tracking issue for this feature is: [#39699](https://github.com/rust-lang/rust/issues/39699).
3+
The tracking issues for this feature are:
4+
5+
* [#39699](https://github.com/rust-lang/rust/issues/39699).
6+
* [#89653](https://github.com/rust-lang/rust/issues/89653).
47

58
------------------------
69

710
This feature allows for use of one of following sanitizers:
811

912
* [AddressSanitizer][clang-asan] a fast memory error detector.
13+
* [ControlFlowIntegrity][clang-cfi] LLVM Control Flow Integrity (CFI) provides
14+
forward-edge control flow protection.
1015
* [HWAddressSanitizer][clang-hwasan] a memory error detector similar to
1116
AddressSanitizer, but based on partial hardware assistance.
1217
* [LeakSanitizer][clang-lsan] a run-time memory leak detector.
1318
* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
1419
* [ThreadSanitizer][clang-tsan] a fast data race detector.
1520

16-
To enable a sanitizer compile with `-Zsanitizer=address`,
21+
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
1722
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or
1823
`-Zsanitizer=thread`.
1924

@@ -177,6 +182,176 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
177182
==39249==ABORTING
178183
```
179184
185+
# ControlFlowIntegrity
186+
187+
The LLVM Control Flow Integrity (CFI) support in the Rust compiler initially
188+
provides forward-edge control flow protection for Rust-compiled code only by
189+
aggregating function pointers in groups identified by their number of arguments.
190+
191+
Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed
192+
binaries" (i.e., for when C or C++ and Rust -compiled code share the same
193+
virtual address space) will be provided in later work by defining and using
194+
compatible type identifiers (see Type metadata in the design document in the
195+
tracking issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
196+
197+
LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto).
198+
199+
## Example
200+
201+
```text
202+
#![feature(asm, naked_functions)]
203+
204+
use std::mem;
205+
206+
fn add_one(x: i32) -> i32 {
207+
x + 1
208+
}
209+
210+
#[naked]
211+
pub extern "C" fn add_two(x: i32) {
212+
// x + 2 preceeded by a landing pad/nop block
213+
unsafe {
214+
asm!(
215+
"
216+
nop
217+
nop
218+
nop
219+
nop
220+
nop
221+
nop
222+
nop
223+
nop
224+
nop
225+
lea rax, [rdi+2]
226+
ret
227+
",
228+
options(noreturn)
229+
);
230+
}
231+
}
232+
233+
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
234+
f(arg) + f(arg)
235+
}
236+
237+
fn main() {
238+
let answer = do_twice(add_one, 5);
239+
240+
println!("The answer is: {}", answer);
241+
242+
println!("With CFI enabled, you should not see the next answer");
243+
let f: fn(i32) -> i32 = unsafe {
244+
// Offsets 0-8 make it land in the landing pad/nop block, and offsets 1-8 are
245+
// invalid branch/call destinations (i.e., within the body of the function).
246+
mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5))
247+
};
248+
let next_answer = do_twice(f, 5);
249+
250+
println!("The next answer is: {}", next_answer);
251+
}
252+
```
253+
Fig. 1. Modified example from the [Advanced Functions and
254+
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
255+
Language][rust-book] book.
256+
257+
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
258+
259+
```shell
260+
$ rustc rust_cfi.rs -o rust_cfi
261+
$ ./rust_cfi
262+
The answer is: 12
263+
With CFI enabled, you should not see the next answer
264+
The next answer is: 14
265+
$
266+
```
267+
Fig. 2. Build and execution of the modified example with LLVM CFI disabled.
268+
269+
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
270+
271+
```shell
272+
$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
273+
$ ./rust_cfi
274+
The answer is: 12
275+
With CFI enabled, you should not see the next answer
276+
Illegal instruction
277+
$
278+
```
279+
Fig. 3. Build and execution of the modified example with LLVM CFI enabled.
280+
281+
When LLVM CFI is enabled, if there are any attempts to change/hijack control
282+
flow using an indirect branch/call to an invalid destination, the execution is
283+
terminated (see Fig. 3).
284+
285+
```rust
286+
use std::mem;
287+
288+
fn add_one(x: i32) -> i32 {
289+
x + 1
290+
}
291+
292+
fn add_two(x: i32, _y: i32) -> i32 {
293+
x + 2
294+
}
295+
296+
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
297+
f(arg) + f(arg)
298+
}
299+
300+
fn main() {
301+
let answer = do_twice(add_one, 5);
302+
303+
println!("The answer is: {}", answer);
304+
305+
println!("With CFI enabled, you should not see the next answer");
306+
let f: fn(i32) -> i32 =
307+
unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
308+
let next_answer = do_twice(f, 5);
309+
310+
println!("The next answer is: {}", next_answer);
311+
}
312+
```
313+
Fig. 4. Another modified example from the [Advanced Functions and
314+
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
315+
Language][rust-book] book.
316+
317+
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
318+
319+
```shell
320+
$ rustc rust_cfi.rs -o rust_cfi
321+
$ ./rust_cfi
322+
The answer is: 12
323+
With CFI enabled, you should not see the next answer
324+
The next answer is: 14
325+
$
326+
```
327+
Fig. 5. Build and execution of the modified example with LLVM CFI disabled.
328+
329+
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
330+
331+
```shell
332+
$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
333+
$ ./rust_cfi
334+
The answer is: 12
335+
With CFI enabled, you should not see the next answer
336+
Illegal instruction
337+
$
338+
```
339+
Fig. 6. Build and execution of the modified example with LLVM CFI enabled.
340+
341+
When LLVM CFI is enabled, if there are any attempts to change/hijack control
342+
flow using an indirect branch/call to a function with different number of
343+
arguments than intended/passed in the call/branch site, the execution is also
344+
terminated (see Fig. 6).
345+
346+
Forward-edge control flow protection not only by aggregating function pointers
347+
in groups identified by their number of arguments, but also their argument
348+
types, will also be provided in later work by defining and using compatible type
349+
identifiers (see Type metadata in the design document in the tracking
350+
issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
351+
352+
[rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
353+
[rust-book]: https://doc.rust-lang.org/book/title-page.html
354+
180355
# HWAddressSanitizer
181356
182357
HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much
@@ -404,12 +579,14 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
404579
405580
* [Sanitizers project page](https://github.com/google/sanitizers/wiki/)
406581
* [AddressSanitizer in Clang][clang-asan]
582+
* [ControlFlowIntegrity in Clang][clang-cfi]
407583
* [HWAddressSanitizer in Clang][clang-hwasan]
408584
* [LeakSanitizer in Clang][clang-lsan]
409585
* [MemorySanitizer in Clang][clang-msan]
410586
* [ThreadSanitizer in Clang][clang-tsan]
411587
412588
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
589+
[clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html
413590
[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
414591
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
415592
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html

0 commit comments

Comments
 (0)