From f792f26a384cb036199ad30a57a2be22a9feef47 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Thu, 31 Mar 2022 22:51:37 -0700 Subject: [PATCH] Update documentation for LLVM CFI support This commit updates the documentation for the LLVM Control Flow Integrity (CFI) support in the Rust compiler (see #95548 and #89653). --- .../src/compiler-flags/sanitizer.md | 112 +++++++++++++----- 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index e83c4d98cc7..448c0a4a3d2 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -191,7 +191,8 @@ Shadow byte legend (one shadow byte represents 8 application bytes): 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. +aggregating function pointers in groups identified by their return and parameter +types. 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 @@ -243,7 +244,7 @@ fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { fn main() { let answer = do_twice(add_one, 5); - println!("The answer is: {answer}"); + println!("The answer is: {}", answer); println!("With CFI enabled, you should not see the next answer"); let f: fn(i32) -> i32 = unsafe { @@ -253,18 +254,18 @@ fn main() { }; let next_answer = do_twice(f, 5); - println!("The next answer is: {next_answer}"); + 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 +$ cargo run --release + Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1) + Finished release [optimized] target(s) in 0.76s + Running `target/release/rust-cfi-1` The answer is: 12 With CFI enabled, you should not see the next answer The next answer is: 14 @@ -272,11 +273,11 @@ $ ``` 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 +$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release + Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1) + Finished release [optimized] target(s) in 3.39s + Running `target/release/rust-cfi-1` The answer is: 12 With CFI enabled, you should not see the next answer Illegal instruction @@ -306,25 +307,25 @@ fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { fn main() { let answer = do_twice(add_one, 5); - println!("The answer is: {answer}"); + 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}"); + 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 +$ cargo run --release + Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2) + Finished release [optimized] target(s) in 0.76s + Running `target/release/rust-cfi-2` The answer is: 12 With CFI enabled, you should not see the next answer The next answer is: 14 @@ -332,11 +333,11 @@ $ ``` 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 +$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release + Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2) + Finished release [optimized] target(s) in 3.38s + Running `target/release/rust-cfi-2` The answer is: 12 With CFI enabled, you should not see the next answer Illegal instruction @@ -346,14 +347,69 @@ 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). +parameters than arguments 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 +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i64) -> i64 { + 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. 7. Another modified example from the [Advanced Functions and +Closures][rust-book-ch19-05] chapter of the [The Rust Programming +Language][rust-book] book. + +```shell + cargo run --release + Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3) + Finished release [optimized] target(s) in 0.74s + Running `target/release/rust-cfi-3` +The answer is: 12 +With CFI enabled, you should not see the next answer +The next answer is: 14 +$ +``` +Fig. 8. Build and execution of the modified example with LLVM CFI disabled. + +```shell +$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release + Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3) + Finished release [optimized] target(s) in 3.40s + Running `target/release/rust-cfi-3` +The answer is: 12 +With CFI enabled, you should not see the next answer +Illegal instruction +$ +``` +Fig. 9. 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 return and +parameter types than the return type expected and arguments intended/passed in +the call/branch site, the execution is also terminated (see Fig. 9). [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