@@ -197,22 +197,26 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
197
197
198
198
# ControlFlowIntegrity
199
199
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.
212
216
213
217
See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
214
218
215
- # # Example
219
+ # # Example 1: Redirecting control flow using an indirect branch/call to an invalid destination
216
220
217
221
` ` ` rust,ignore (making doc tests pass cross-platform is hard)
218
222
#! [feature(naked_functions)]
@@ -239,7 +243,7 @@ pub extern "C" fn add_two(x: i32) {
239
243
nop
240
244
nop
241
245
nop
242
- lea eax, [edi +2]
246
+ lea eax, [rdi +2]
243
247
ret
244
248
" ,
245
249
options(noreturn)
@@ -258,47 +262,50 @@ fn main() {
258
262
259
263
println! (" With CFI enabled, you should not see the next answer" );
260
264
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).
263
268
mem::transmute::< * const u8, fn(i32) -> i32> (( add_two as * const u8 ).offset(5 ))
264
269
};
265
270
let next_answer = do_twice(f, 5);
266
271
267
272
println! (" The next answer is: {}" , next_answer);
268
273
}
269
274
` ` `
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).
273
277
274
278
` ` ` shell
275
279
$ cargo run --release
276
280
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
278
282
Running ` target/release/rust-cfi-1`
279
283
The answer is: 12
280
284
With CFI enabled, you should not see the next answer
281
285
The next answer is: 14
282
286
$
283
287
` ` `
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.
285
289
286
290
` ` ` 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
+ ...
288
293
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`
291
296
The answer is: 12
292
297
With CFI enabled, you should not see the next answer
293
298
Illegal instruction
294
299
$
295
300
` ` `
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.
297
302
298
303
When LLVM CFI is enabled, if there are any attempts to change/hijack control
299
304
flow using an indirect branch/call to an invalid destination, the execution is
300
305
terminated (see Fig. 3).
301
306
307
+ # # Example 2: Redirecting control flow using an indirect branch/call to a function with a different number of parameters
308
+
302
309
` ` ` rust
303
310
use std::mem;
304
311
@@ -327,39 +334,42 @@ fn main() {
327
334
println! (" The next answer is: {}" , next_answer);
328
335
}
329
336
` ` `
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 .
333
340
334
341
` ` ` shell
335
342
$ cargo run --release
336
343
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
338
345
Running ` target/release/rust-cfi-2`
339
346
The answer is: 12
340
347
With CFI enabled, you should not see the next answer
341
348
The next answer is: 14
342
349
$
343
350
` ` `
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.
345
352
346
353
` ` ` 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
+ ...
348
356
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`
351
359
The answer is: 12
352
360
With CFI enabled, you should not see the next answer
353
361
Illegal instruction
354
362
$
355
363
` ` `
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.
357
365
358
366
When LLVM CFI is enabled, if there are any attempts to change/hijack control
359
367
flow using an indirect branch/call to a function with different number of
360
368
parameters than arguments intended/passed in the call/branch site, the
361
369
execution is also terminated (see Fig. 6).
362
370
371
+ # # Example 3: Redirecting control flow using an indirect branch/call to a function with different return and parameter types
372
+
363
373
` ` ` rust
364
374
use std::mem;
365
375
@@ -388,42 +398,46 @@ fn main() {
388
398
println! (" The next answer is: {}" , next_answer);
389
399
}
390
400
` ` `
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 .
394
404
395
405
` ` ` shell
396
406
$ cargo run --release
397
407
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
399
409
Running ` target/release/rust-cfi-3`
400
410
The answer is: 12
401
411
With CFI enabled, you should not see the next answer
402
412
The next answer is: 14
403
413
$
404
414
` ` `
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.
406
416
407
417
` ` ` 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
+ ...
409
420
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`
412
423
The answer is: 12
413
424
With CFI enabled, you should not see the next answer
414
425
Illegal instruction
415
426
$
416
427
` ` `
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.
418
429
419
430
When LLVM CFI is enabled, if there are any attempts to change/hijack control
420
431
flow using an indirect branch/call to a function with different return and
421
432
parameter types than the return type expected and arguments intended/passed in
422
433
the call/branch site, the execution is also terminated (see Fig. 9).
423
434
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
+
424
437
` ` ` ignore (cannot-test-this-because-uses-custom-build)
425
438
int
426
- do_twice(int (* fn)(int), int arg) {
439
+ do_twice(int (* fn)(int), int arg)
440
+ {
427
441
return fn(arg) + fn(arg);
428
442
}
429
443
` ` `
@@ -459,54 +473,49 @@ fn main() {
459
473
println! (" The next answer is: {}" , next_answer);
460
474
}
461
475
` ` `
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 .
465
479
466
480
` ` ` shell
467
481
$ 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
475
489
The answer is: 12
476
490
With CFI enabled, you should not see the next answer
477
491
The next answer is: 14
478
492
$
479
493
` ` `
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.
481
495
482
496
` ` ` shell
483
497
$ 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
491
506
The answer is: 12
492
507
With CFI enabled, you should not see the next answer
493
508
Illegal instruction
494
509
$
495
510
` ` `
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).
510
519
511
520
# HWAddressSanitizer
512
521
0 commit comments