Skip to content

Commit

Permalink
Add documentation for LLVM CFI support
Browse files Browse the repository at this point in the history
This commit adds initial documentation for LLVM Control Flow Integrity
(CFI) support to the Rust compiler (see #89652 and #89653).
  • Loading branch information
rcvalle committed Oct 27, 2021
1 parent 5d30e93 commit c5708ca
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 19 deletions.
40 changes: 23 additions & 17 deletions src/doc/rustc/src/exploit-mitigations.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,9 @@ equivalent.
<tr>
<td>Forward-edge control flow protection
</td>
<td>No
<td>Yes
</td>
<td>
<td>Nightly
</td>
</tr>
<tr>
Expand Down Expand Up @@ -465,24 +465,27 @@ implementations such as [LLVM ControlFlowIntegrity
commercially available [grsecurity/PaX Reuse Attack Protector
(RAP)](https://grsecurity.net/rap_faq).

The Rust compiler does not support forward-edge control flow protection on
Linux<sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
class="footnote">6</a></sup>. There is work currently ongoing to add support
for the [sanitizers](https://github.com/google/sanitizers)[40], which may or
may not include support for LLVM CFI.
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"
class="footnote">6</a></sup>.

```text
$ readelf -s target/release/hello-rust | grep __cfi_init
$ 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
...
```
Fig. 15. Checking if LLVM CFI is enabled for a given binary.
Fig. 15. Checking if LLVM CFI is enabled for a given binary[41].

The presence of the `__cfi_init` symbol (and references to `__cfi_check`)
indicates that LLVM CFI (i.e., forward-edge control flow protection) is
enabled for a given binary. Conversely, the absence of the `__cfi_init`
symbol (and references to `__cfi_check`) indicates that LLVM CFI is not
enabled for a given binary (see Fig. 15).
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 control
flow protection) is enabled for a given binary. Conversely, the absence of
symbols suffixed with ".cfi" or the `__cfi_init` symbol (and references to
`__cfi_check`) indicates that LLVM CFI is not enabled for a given binary (see
Fig. 15).

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

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

40. J. Aparicio. 2017. “Tracking issue for sanitizer support #39699.”
<https://github.com/rust-lang/rust/issues/39699>.
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/beta/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity>.
181 changes: 179 additions & 2 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
# `sanitizer`

The tracking issue for this feature is: [#39699](https://github.com/rust-lang/rust/issues/39699).
The tracking issues for this feature are:

* [#39699](https://github.com/rust-lang/rust/issues/39699).
* [#89653](https://github.com/rust-lang/rust/issues/89653).

------------------------

This feature allows for use of one of following sanitizers:

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

To enable a sanitizer compile with `-Zsanitizer=address`,
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or
`-Zsanitizer=thread`.

Expand Down Expand Up @@ -177,6 +182,176 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
==39249==ABORTING
```
# ControlFlowIntegrity
The LLVM Control Flow Integrity (CFI) support in the Rust compiler initially
provides forward-edge control flow protection for Rust-compiled code only by
aggregating function pointers in groups identified by their number of arguments.
Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed
binaries" (i.e., for when C or C++ and Rust -compiled code share the same
virtual address space) will be provided in later work by defining and using
compatible type identifiers (see Type metadata in the design document in the
tracking issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto).
## Example
```text
#![feature(asm, naked_functions)]
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
#[naked]
pub extern "C" fn add_two(x: i32) {
// x + 2 preceeded by a landing pad/nop block
unsafe {
asm!(
"
nop
nop
nop
nop
nop
nop
nop
nop
nop
lea rax, [rdi+2]
ret
",
options(noreturn)
);
}
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 = unsafe {
// Offsets 0-8 make it land in the landing pad/nop block, and offsets 1-8 are
// invalid branch/call destinations (i.e., within the body of the function).
mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5))
};
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
```
Fig. 1. Modified example from the [Advanced Functions and
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc rust_cfi.rs -o rust_cfi
$ ./rust_cfi
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
Fig. 2. Build and execution of the modified example with LLVM CFI disabled.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
$ ./rust_cfi
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
Fig. 3. Build and execution of the modified example with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to an invalid destination, the execution is
terminated (see Fig. 3).
```rust
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
fn add_two(x: i32, _y: i32) -> i32 {
x + 2
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
```
Fig. 4. Another modified example from the [Advanced Functions and
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc rust_cfi.rs -o rust_cfi
$ ./rust_cfi
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
Fig. 5. Build and execution of the modified example with LLVM CFI disabled.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
$ ./rust_cfi
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
Fig. 6. Build and execution of the modified example with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to a function with different number of
arguments than intended/passed in the call/branch site, the execution is also
terminated (see Fig. 6).
Forward-edge control flow protection not only by aggregating function pointers
in groups identified by their number of arguments, but also their argument
types, will also be provided in later work by defining and using compatible type
identifiers (see Type metadata in the design document in the tracking
issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
[rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
[rust-book]: https://doc.rust-lang.org/book/title-page.html
# HWAddressSanitizer
HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much
Expand Down Expand Up @@ -404,12 +579,14 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
* [Sanitizers project page](https://github.com/google/sanitizers/wiki/)
* [AddressSanitizer in Clang][clang-asan]
* [ControlFlowIntegrity in Clang][clang-cfi]
* [HWAddressSanitizer in Clang][clang-hwasan]
* [LeakSanitizer in Clang][clang-lsan]
* [MemorySanitizer in Clang][clang-msan]
* [ThreadSanitizer in Clang][clang-tsan]
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
[clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html
[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
Expand Down

0 comments on commit c5708ca

Please sign in to comment.