Skip to content

Commit eea2614

Browse files
committed
Auto merge of #115200 - rcvalle:rust-cfi-fix-115199, r=workingjubilee
Disable CFI for core and std CFI violations Work around #115199 by temporarily disabling CFI for core and std CFI violations to allow the user rebuild and use both core and std with CFI enabled using the Cargo build-std feature.
2 parents f306362 + abece4f commit eea2614

File tree

5 files changed

+123
-88
lines changed

5 files changed

+123
-88
lines changed

library/core/src/fmt/rt.rs

+4
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ impl<'a> Argument<'a> {
133133
Self::new(x, USIZE_MARKER)
134134
}
135135

136+
// FIXME: Transmuting formatter in new and indirectly branching to/calling
137+
// it here is an explicit CFI violation.
138+
#[allow(inline_no_sanitize)]
139+
#[no_sanitize(cfi, kcfi)]
136140
#[inline(always)]
137141
pub(super) fn fmt(&self, f: &mut Formatter<'_>) -> Result {
138142
(self.formatter)(self.value, f)

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@
238238
#![feature(negative_impls)]
239239
#![feature(never_type)]
240240
#![feature(no_core)]
241+
#![feature(no_sanitize)]
241242
#![feature(platform_intrinsics)]
242243
#![feature(prelude_import)]
243244
#![feature(repr_simd)]

library/std/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
#![feature(allow_internal_unstable)]
271271
#![feature(c_unwind)]
272272
#![feature(cfg_target_thread_local)]
273+
#![feature(cfi_encoding)]
273274
#![feature(concat_idents)]
274275
#![feature(const_mut_refs)]
275276
#![feature(const_trait_impl)]
@@ -292,6 +293,7 @@
292293
#![feature(needs_panic_runtime)]
293294
#![feature(negative_impls)]
294295
#![feature(never_type)]
296+
#![feature(no_sanitize)]
295297
#![feature(platform_intrinsics)]
296298
#![feature(prelude_import)]
297299
#![feature(rustc_attrs)]

library/std/src/sys/unix/thread_local_dtor.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,47 @@
1111
// Note, however, that we run on lots older linuxes, as well as cross
1212
// compiling from a newer linux to an older linux, so we also have a
1313
// fallback implementation to use as well.
14+
#[allow(unexpected_cfgs)]
1415
#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox", target_os = "hurd"))]
16+
// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
17+
// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
18+
#[no_sanitize(cfi, kcfi)]
1519
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
1620
use crate::mem;
1721
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
1822

23+
/// This is necessary because the __cxa_thread_atexit_impl implementation
24+
/// std links to by default may be a C or C++ implementation that was not
25+
/// compiled using the Clang integer normalization option.
26+
#[cfg(not(sanitizer_cfi_normalize_integers))]
27+
#[cfi_encoding = "i"]
28+
#[repr(transparent)]
29+
pub struct c_int(pub libc::c_int);
30+
1931
extern "C" {
2032
#[linkage = "extern_weak"]
2133
static __dso_handle: *mut u8;
2234
#[linkage = "extern_weak"]
23-
static __cxa_thread_atexit_impl: *const libc::c_void;
35+
static __cxa_thread_atexit_impl: Option<
36+
extern "C" fn(
37+
unsafe extern "C" fn(*mut libc::c_void),
38+
*mut libc::c_void,
39+
*mut libc::c_void,
40+
) -> c_int,
41+
>;
2442
}
25-
if !__cxa_thread_atexit_impl.is_null() {
26-
type F = unsafe extern "C" fn(
27-
dtor: unsafe extern "C" fn(*mut u8),
28-
arg: *mut u8,
29-
dso_handle: *mut u8,
30-
) -> libc::c_int;
31-
mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)(
32-
dtor,
33-
t,
34-
&__dso_handle as *const _ as *mut _,
35-
);
43+
44+
if let Some(f) = __cxa_thread_atexit_impl {
45+
unsafe {
46+
f(
47+
mem::transmute::<
48+
unsafe extern "C" fn(*mut u8),
49+
unsafe extern "C" fn(*mut libc::c_void),
50+
>(dtor),
51+
t.cast(),
52+
&__dso_handle as *const _ as *mut _,
53+
);
54+
}
3655
return;
3756
}
3857
register_dtor_fallback(t, dtor);

src/doc/unstable-book/src/compiler-flags/sanitizer.md

+85-76
Original file line numberDiff line numberDiff line change
@@ -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
213217
See 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`
279283
The answer is: 12
280284
With CFI enabled, you should not see the next answer
281285
The 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`
291296
The answer is: 12
292297
With CFI enabled, you should not see the next answer
293298
Illegal 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
298303
When LLVM CFI is enabled, if there are any attempts to change/hijack control
299304
flow using an indirect branch/call to an invalid destination, the execution is
300305
terminated (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
303310
use 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`
339346
The answer is: 12
340347
With CFI enabled, you should not see the next answer
341348
The 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`
351359
The answer is: 12
352360
With CFI enabled, you should not see the next answer
353361
Illegal 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
358366
When LLVM CFI is enabled, if there are any attempts to change/hijack control
359367
flow using an indirect branch/call to a function with different number of
360368
parameters than arguments intended/passed in the call/branch site, the
361369
execution 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
364374
use 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`
400410
The answer is: 12
401411
With CFI enabled, you should not see the next answer
402412
The 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`
412423
The answer is: 12
413424
With CFI enabled, you should not see the next answer
414425
Illegal 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
419430
When LLVM CFI is enabled, if there are any attempts to change/hijack control
420431
flow using an indirect branch/call to a function with different return and
421432
parameter types than the return type expected and arguments intended/passed in
422433
the 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)
425438
int
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
475489
The answer is: 12
476490
With CFI enabled, you should not see the next answer
477491
The 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
491506
The answer is: 12
492507
With CFI enabled, you should not see the next answer
493508
Illegal 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

Comments
 (0)