@@ -197,22 +197,26 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
197197
198198# ControlFlowIntegrity
199199
200- The LLVM Control Flow Integrity (CFI) support in the Rust compiler provides
201- forward-edge control flow protection for both Rust-compiled code only and for C
202- or C++ and Rust -compiled code mixed-language binaries, also known as “mixed
203- binaries” (i.e., for when C or C++ and Rust -compiled code share the same
204- virtual address space), by aggregating function pointers in groups identified by
205- their return and parameter types.
206-
207- LLVM CFI can be enabled with ` -Zsanitizer=cfi` and requires LTO (i.e., ` -Clto` ).
208- Cross-language LLVM CFI can be enabled with ` -Zsanitizer=cfi` , and requires the
209- ` -Zsanitizer-cfi-normalize-integers` option to be used with Clang
210- ` -fsanitize-cfi-icall-normalize-integers` for normalizing integer types, and
211- proper (i.e., non-rustc) LTO (i.e., ` -Clinker-plugin-lto` ).
200+ The LLVM CFI support in the Rust compiler provides forward-edge control flow
201+ protection for both Rust-compiled code only and for C or C++ and Rust -compiled
202+ code mixed-language binaries, also known as “mixed binaries” (i.e., for when C
203+ or C++ and Rust -compiled code share the same virtual address space), by
204+ aggregating function pointers in groups identified by their return and parameter
205+ types.
206+
207+ LLVM CFI can be enabled with ` -Zsanitizer=cfi` and requires LTO (i.e.,
208+ ` -Clinker-plugin-lto` or ` -Clto` ). Cross-language LLVM CFI can be enabled with
209+ ` -Zsanitizer=cfi` , and requires the ` -Zsanitizer-cfi-normalize-integers` option
210+ to be used with Clang ` -fsanitize-cfi-icall-experimental-normalize-integers`
211+ option for cross-language LLVM CFI support, and proper (i.e., non-rustc) LTO
212+ (i.e., ` -Clinker-plugin-lto` ).
213+
214+ It is recommended to rebuild the standard library with CFI enabled by using the
215+ Cargo build-std feature (i.e., ` -Zbuild-std` ) when enabling CFI.
212216
213217See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
214218
215- # # Example
219+ # # Example 1: Redirecting control flow using an indirect branch/call to an invalid destination
216220
217221` ` ` rust,ignore (making doc tests pass cross-platform is hard)
218222#! [feature(naked_functions)]
@@ -239,7 +243,7 @@ pub extern "C" fn add_two(x: i32) {
239243 nop
240244 nop
241245 nop
242- lea eax, [edi +2]
246+ lea eax, [rdi +2]
243247 ret
244248 " ,
245249 options(noreturn)
@@ -258,47 +262,50 @@ fn main() {
258262
259263 println! (" With CFI enabled, you should not see the next answer" );
260264 let f: fn(i32) -> i32 = unsafe {
261- // Offsets 0-8 make it land in the landing pad/nop block, and offsets 1-8 are
262- // invalid branch/call destinations (i.e., within the body of the function).
265+ // Offset 0 is a valid branch/call destination (i.e., the function entry
266+ // point), but offsets 1-8 within the landing pad/nop block are invalid
267+ // branch/call destinations (i.e., within the body of the function).
263268 mem::transmute::< * const u8, fn(i32) -> i32> (( add_two as * const u8 ).offset(5 ))
264269 };
265270 let next_answer = do_twice(f, 5);
266271
267272 println! (" The next answer is: {}" , next_answer);
268273}
269274` ` `
270- Fig. 1. Modified example from the [Advanced Functions and
271- Closures][rust-book-ch19-05] chapter of the [The Rust Programming
272- Language][rust-book] book.
275+ Fig. 1. Redirecting control flow using an indirect branch/call to an invalid
276+ destination (i.e., within the body of the function).
273277
274278` ` ` shell
275279$ cargo run --release
276280 Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
277- Finished release [optimized] target(s) in 0.76s
281+ Finished release [optimized] target(s) in 0.42s
278282 Running ` target/release/rust-cfi-1`
279283The answer is: 12
280284With CFI enabled, you should not see the next answer
281285The next answer is: 14
282286$
283287` ` `
284- Fig. 2. Build and execution of the modified example with LLVM CFI disabled.
288+ Fig. 2. Build and execution of Fig. 1 with LLVM CFI disabled.
285289
286290` ` ` shell
287- $ RUSTFLAGS=" -Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
291+ $ RUSTFLAGS=" -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
292+ ...
288293 Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
289- Finished release [optimized] target(s) in 3.39s
290- Running ` target/release/rust-cfi-1`
294+ Finished release [optimized] target(s) in 1m 08s
295+ Running ` target/x86_64-unknown-linux-gnu/ release/rust-cfi-1`
291296The answer is: 12
292297With CFI enabled, you should not see the next answer
293298Illegal instruction
294299$
295300` ` `
296- Fig. 3. Build and execution of the modified example with LLVM CFI enabled.
301+ Fig. 3. Build and execution of Fig. 1 with LLVM CFI enabled.
297302
298303When LLVM CFI is enabled, if there are any attempts to change/hijack control
299304flow using an indirect branch/call to an invalid destination, the execution is
300305terminated (see Fig. 3).
301306
307+ # # Example 2: Redirecting control flow using an indirect branch/call to a function with a different number of parameters
308+
302309` ` ` rust
303310use std::mem;
304311
@@ -327,39 +334,42 @@ fn main() {
327334 println! (" The next answer is: {}" , next_answer);
328335}
329336` ` `
330- Fig. 4. Another modified example from the [Advanced Functions and
331- Closures][rust-book-ch19-05] chapter of the [The Rust Programming
332- Language][rust-book] book .
337+ Fig. 4. Redirecting control flow using an indirect branch/call to a function
338+ with a different number of parameters than arguments intended/passed in the
339+ call/branch site .
333340
334341` ` ` shell
335342$ cargo run --release
336343 Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
337- Finished release [optimized] target(s) in 0.76s
344+ Finished release [optimized] target(s) in 0.43s
338345 Running ` target/release/rust-cfi-2`
339346The answer is: 12
340347With CFI enabled, you should not see the next answer
341348The next answer is: 14
342349$
343350` ` `
344- Fig. 5. Build and execution of the modified example with LLVM CFI disabled.
351+ Fig. 5. Build and execution of Fig. 4 with LLVM CFI disabled.
345352
346353` ` ` shell
347- $ RUSTFLAGS=" -Cembed-bitcode=yes -Clto -Zsanitizer=cfi" cargo run --release
354+ $ RUSTFLAGS=" -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
355+ ...
348356 Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
349- Finished release [optimized] target(s) in 3.38s
350- Running ` target/release/rust-cfi-2`
357+ Finished release [optimized] target(s) in 1m 08s
358+ Running ` target/x86_64-unknown-linux-gnu/ release/rust-cfi-2`
351359The answer is: 12
352360With CFI enabled, you should not see the next answer
353361Illegal instruction
354362$
355363` ` `
356- Fig. 6. Build and execution of the modified example with LLVM CFI enabled.
364+ Fig. 6. Build and execution of Fig. 4 with LLVM CFI enabled.
357365
358366When LLVM CFI is enabled, if there are any attempts to change/hijack control
359367flow using an indirect branch/call to a function with different number of
360368parameters than arguments intended/passed in the call/branch site, the
361369execution is also terminated (see Fig. 6).
362370
371+ # # Example 3: Redirecting control flow using an indirect branch/call to a function with different return and parameter types
372+
363373` ` ` rust
364374use std::mem;
365375
@@ -388,42 +398,46 @@ fn main() {
388398 println! (" The next answer is: {}" , next_answer);
389399}
390400` ` `
391- Fig. 7. Another modified example from the [Advanced Functions and
392- Closures][rust-book-ch19-05] chapter of the [The Rust Programming
393- Language][rust-book] book .
401+ Fig. 7. Redirecting control flow using an indirect branch/call to a function
402+ with different return and parameter types than the return type expected and
403+ arguments intended/passed at the call/branch site .
394404
395405` ` ` shell
396406$ cargo run --release
397407 Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
398- Finished release [optimized] target(s) in 0.74s
408+ Finished release [optimized] target(s) in 0.44s
399409 Running ` target/release/rust-cfi-3`
400410The answer is: 12
401411With CFI enabled, you should not see the next answer
402412The next answer is: 14
403413$
404414` ` `
405- Fig. 8. Build and execution of the modified example with LLVM CFI disabled.
415+ Fig. 8. Build and execution of Fig. 7 with LLVM CFI disabled.
406416
407417` ` ` shell
408- $ RUSTFLAGS=" -Cembed-bitcode=yes -Clto -Zsanitizer=cfi" cargo run --release
418+ $ RUSTFLAGS=" -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
419+ ...
409420 Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
410- Finished release [optimized] target(s) in 3.40s
411- Running ` target/release/rust-cfi-3`
421+ Finished release [optimized] target(s) in 1m 07s
422+ Running ` target/x86_64-unknown-linux-gnu/ release/rust-cfi-3`
412423The answer is: 12
413424With CFI enabled, you should not see the next answer
414425Illegal instruction
415426$
416427` ` `
417- Fig. 9. Build and execution of the modified example with LLVM CFI enabled.
428+ Fig. 9. Build and execution of Fig. 7 with LLVM CFI enabled.
418429
419430When LLVM CFI is enabled, if there are any attempts to change/hijack control
420431flow using an indirect branch/call to a function with different return and
421432parameter types than the return type expected and arguments intended/passed in
422433the call/branch site, the execution is also terminated (see Fig. 9).
423434
435+ # # Example 4: Redirecting control flow using an indirect branch/call to a function with different return and parameter types across the FFI boundary
436+
424437` ` ` ignore (cannot-test-this-because-uses-custom-build)
425438int
426- do_twice(int (* fn)(int), int arg) {
439+ do_twice(int (* fn)(int), int arg)
440+ {
427441 return fn(arg) + fn(arg);
428442}
429443` ` `
@@ -459,54 +473,49 @@ fn main() {
459473 println! (" The next answer is: {}" , next_answer);
460474}
461475` ` `
462- Fig. 11. Another modified example from the [Advanced Functions and
463- Closures][rust-book-ch19-05] chapter of the [The Rust Programming
464- Language][rust-book] book .
476+ Fig. 11. Redirecting control flow using an indirect branch/call to a function
477+ with different return and parameter types than the return type expected and
478+ arguments intended/passed in the call/branch site, across the FFI boundary .
465479
466480` ` ` shell
467481$ make
468- mkdir -p target/debug
469- clang -I. -Isrc -Wall -flto -fvisibility=hidden -c -emit-llvm src/foo.c -o target/debug /libfoo.bc
470- llvm-ar rcs target/debug /libfoo.a target/debug /libfoo.bc
471- RUSTFLAGS=" -L./target/debug -Clinker-plugin-lto -Clinker =clang -Clink-arg=-fuse-ld=lld" cargo build
472- Compiling main v0.1.0 (/home/rcvalle/rust-cross- cfi-1 )
473- Finished dev [unoptimized + debuginfo ] target(s) in 0.45s
474- $ ./target/debug/main
482+ mkdir -p target/release
483+ clang -I. -Isrc -Wall -c src/foo.c -o target/release /libfoo.o
484+ llvm-ar rcs target/release /libfoo.a target/release /libfoo.o
485+ RUSTFLAGS=" -L./target/release -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release
486+ Compiling rust-cfi-4 v0.1.0 (/home/rcvalle/rust-cfi-4 )
487+ Finished release [optimized ] target(s) in 0.49s
488+ $ ./target/release/rust-cfi-4
475489The answer is: 12
476490With CFI enabled, you should not see the next answer
477491The next answer is: 14
478492$
479493` ` `
480- Fig. 12. Build and execution of the modified example with LLVM CFI disabled.
494+ Fig. 12. Build and execution of Figs. 10–11 with LLVM CFI disabled.
481495
482496` ` ` shell
483497$ make
484- mkdir -p target/debug
485- 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
486- llvm-ar rcs target/debug/libfoo.a target/debug/libfoo.bc
487- RUSTFLAGS=" -L./target/debug -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers" cargo build
488- Compiling main v0.1.0 (/home/rcvalle/rust-cross-cfi-1)
489- Finished dev [unoptimized + debuginfo] target(s) in 0.45s
490- $ ./target/debug/main
498+ mkdir -p target/release
499+ clang -I. -Isrc -Wall -flto -fsanitize=cfi -fsanitize-cfi-icall-experimental-normalize-integers -fvisibility=hidden -c -emit-llvm src/foo.c -o target/release/libfoo.bc
500+ llvm-ar rcs target/release/libfoo.a target/release/libfoo.bc
501+ RUSTFLAGS=" -L./target/release -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers" cargo build -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
502+ ...
503+ Compiling rust-cfi-4 v0.1.0 (/home/rcvalle/rust-cfi-4)
504+ Finished release [optimized] target(s) in 1m 06s
505+ $ ./target/x86_64-unknown-linux-gnu/release/rust-cfi-4
491506The answer is: 12
492507With CFI enabled, you should not see the next answer
493508Illegal instruction
494509$
495510` ` `
496- Fig. 13. Build and execution of the modified example with LLVM CFI enabled.
497-
498- When LLVM CFI is enabled, if there are any attempts to change/hijack control
499- flow using an indirect branch/call to a function with different return and
500- parameter types than the return type expected and arguments intended/passed in
501- the call/branch site, even across the FFI boundary and for extern " C" function
502- types indirectly called (i.e., callbacks/function pointers) across the FFI
503- boundary, in C or C++ and Rust -compiled code mixed-language binaries, also
504- known as “mixed binaries” (i.e., for when C or C++ and Rust -compiled code share
505- the same virtual address space), the execution is also terminated (see Fig. 13).
506-
507-
508- [rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
509- [rust-book]: https://doc.rust-lang.org/book/title-page.html
511+ Fig. 13. Build and execution of FIgs. 10–11 with LLVM CFI enabled.
512+
513+ When LLVM CFI is enabled, if there are any attempts to redirect control flow
514+ using an indirect branch/call to a function with different return and parameter
515+ types than the return type expected and arguments intended/passed in the
516+ call/branch site, even across the FFI boundary and for extern " C" function types
517+ indirectly called (i.e., callbacks/function pointers) across the FFI boundary,
518+ the execution is also terminated (see Fig. 13).
510519
511520# HWAddressSanitizer
512521
0 commit comments