Skip to content

Commit

Permalink
Update documentation for LLVM CFI support
Browse files Browse the repository at this point in the history
This commit updates the documentation for the LLVM Control Flow
Integrity (CFI) support in the Rust compiler.
  • Loading branch information
rcvalle committed Apr 26, 2023
1 parent 759e4d3 commit 77b4492
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 17 deletions.
118 changes: 101 additions & 17 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,18 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
# 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 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
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).
The LLVM Control Flow Integrity (CFI) support in the Rust compiler provides
forward-edge control flow protection for both Rust-compiled code only and for C
or C++ and Rust -compiled code mixed-language binaries, also known as “mixed
binaries” (i.e., for when C or C++ and Rust -compiled code share the same
virtual address space), by aggregating function pointers in groups identified by
their return and parameter types.
LLVM CFI can be enabled with `-Zsanitizer=cfi` and requires LTO (i.e., `-Clto`).
Cross-language LLVM CFI can be enabled with `-Zsanitizer=cfi`, and requires the
`-Zsanitizer-cfi-normalize-integers` option to be used with Clang
`-fsanitize-cfi-icall-normalize-integers` for normalizing integer types, and
proper (i.e., non-rustc) LTO (i.e., `-Clinker-plugin-lto`).
See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
Expand Down Expand Up @@ -343,7 +343,7 @@ $
Fig. 5. Build and execution of the modified example with LLVM CFI disabled.
```shell
$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
$ RUSTFLAGS="-Cembed-bitcode=yes -Clto -Zsanitizer=cfi" 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`
Expand Down Expand Up @@ -392,7 +392,7 @@ Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
```shell
cargo run --release
$ 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`
Expand All @@ -404,7 +404,7 @@ $
Fig. 8. Build and execution of the modified example with LLVM CFI disabled.
```shell
$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
$ RUSTFLAGS="-Cembed-bitcode=yes -Clto -Zsanitizer=cfi" 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`
Expand All @@ -420,8 +420,92 @@ 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]: ../../book/ch19-05-advanced-functions-and-closures.html
[rust-book]: ../../book/title-page.html
```ignore (cannot-test-this-because-uses-custom-build)
int
do_twice(int (*fn)(int), int arg) {
return fn(arg) + fn(arg);
}
```
Fig. 10. Example C library.
```ignore (cannot-test-this-because-uses-custom-build)
use std::mem;
#[link(name = "foo")]
extern "C" {
fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32;
}
unsafe extern "C" fn add_one(x: i32) -> i32 {
x + 1
}
unsafe extern "C" fn add_two(x: i64) -> i64 {
x + 2
}
fn main() {
let answer = unsafe { do_twice(add_one, 5) };
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: unsafe extern "C" fn(i32) -> i32 = unsafe {
mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8)
};
let next_answer = unsafe { do_twice(f, 5) };
println!("The next answer is: {}", next_answer);
}
```
Fig. 11. Another modified example from the [Advanced Functions and
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
```shell
$ make
mkdir -p target/debug
clang -I. -Isrc -Wall -flto -fvisibility=hidden -c -emit-llvm src/foo.c -o target/debug/libfoo.bc
llvm-ar rcs target/debug/libfoo.a target/debug/libfoo.bc
RUSTFLAGS="-L./target/debug -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build
Compiling main v0.1.0 (/home/rcvalle/rust-cross-cfi-1)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
$ ./target/debug/main
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
Fig. 12. Build and execution of the modified example with LLVM CFI disabled.
```shell
$ make
mkdir -p target/debug
clang -I. -Isrc -Wall -flto -fvisibility=hidden -fsanitize=cfi -fsanitize-cfi-icall-normalize-integers -c -emit-llvm src/foo.c -o target/debug/libfoo.bc
llvm-ar rcs target/debug/libfoo.a target/debug/libfoo.bc
RUSTFLAGS="-L./target/debug -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers" cargo build
Compiling main v0.1.0 (/home/rcvalle/rust-cross-cfi-1)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
$ ./target/debug/main
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
Fig. 13. 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, even across the FFI boundary and for extern "C" function
types indirectly called (i.e., callbacks/function pointers) across the FFI
boundary, in C or C++ and Rust -compiled code mixed-language binaries, also
known as “mixed binaries” (i.e., for when C or C++ and Rust -compiled code share
the same virtual address space), the execution is also terminated (see Fig. 13).
[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
Expand Down
25 changes: 25 additions & 0 deletions src/doc/unstable-book/src/language-features/cfi-encoding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# `cfi_encoding`

The tracking issue for this feature is: [#89653]

[#89653]: https://github.com/rust-lang/rust/issues/89653

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

The `cfi_encoding` feature allows the user to define a CFI encoding for a type.
It allows the user to use a different names for types that otherwise would be
required to have the same name as used in externally defined C functions.

## Examples

```rust
#![feature(cfi_encoding, extern_types)]

#[cfi_encoding = "3Foo"]
pub struct Type1(i32);

extern {
#[cfi_encoding = "3Bar"]
type Type2;
}
```

0 comments on commit 77b4492

Please sign in to comment.