Skip to content

Commit 473a70d

Browse files
committed
CFI: Support function pointers for trait methods
Adds support for both CFI and KCFI for attaching concrete and abstract types to functions. KCFI does this through generation of `ReifyShim` on any function pointer that could go in a vtable, and checking the `ReifyReason` when emitting the instance. CFI does this by attaching both the concrete and abstract type to every instance. TypeID codegen tests are switched to be anchored on the left rather than the right in order to allow emission of additional type attachments. Fixes #115953
1 parent 6aa89f6 commit 473a70d

File tree

4 files changed

+63
-10
lines changed

4 files changed

+63
-10
lines changed

compiler/rustc_middle/src/ty/instance.rs

+18
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,24 @@ impl<'tcx> Instance<'tcx> {
517517
debug!(" => fn pointer created for virtual call");
518518
resolved.def = InstanceDef::ReifyShim(def_id, reason);
519519
}
520+
// Reify `Trait::method` implementations if KCFI is enabled
521+
// FIXME(maurer) only reify it if it is a vtable-safe function
522+
_ if tcx.sess.is_sanitizer_kcfi_enabled()
523+
&& tcx.associated_item(def_id).trait_item_def_id.is_some() =>
524+
{
525+
// If this function could also go in a vtable, we need to `ReifyShim` it with
526+
// KCFI because it can only attach one type per function.
527+
resolved.def = InstanceDef::ReifyShim(resolved.def_id(), reason)
528+
}
529+
// Reify `::call`-like method implementations if KCFI is enabled
530+
_ if tcx.sess.is_sanitizer_kcfi_enabled()
531+
&& tcx.is_closure_like(resolved.def_id()) =>
532+
{
533+
// Reroute through a reify via the *unresolved* instance. The resolved one can't
534+
// be directly reified because it's closure-like. The reify can handle the
535+
// unresolved instance.
536+
resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args }
537+
}
520538
_ => {}
521539
}
522540

compiler/rustc_symbol_mangling/src/typeid.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
55
/// see design document in the tracking issue #89653.
66
use bitflags::bitflags;
7-
use rustc_middle::ty::{Instance, Ty, TyCtxt};
7+
use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt};
88
use rustc_target::abi::call::FnAbi;
99
use std::hash::Hasher;
1010
use twox_hash::XxHash64;
@@ -67,8 +67,13 @@ pub fn kcfi_typeid_for_fnabi<'tcx>(
6767
pub fn kcfi_typeid_for_instance<'tcx>(
6868
tcx: TyCtxt<'tcx>,
6969
instance: Instance<'tcx>,
70-
options: TypeIdOptions,
70+
mut options: TypeIdOptions,
7171
) -> u32 {
72+
// If we receive a `ReifyShim` intended to produce a function pointer, we need to remain
73+
// concrete - abstraction is for vtables.
74+
if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) {
75+
options.remove(TypeIdOptions::ERASE_SELF_TYPE);
76+
}
7277
// A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the
7378
// xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
7479
let mut hash: XxHash64 = Default::default();

tests/ui/sanitizer/cfi-closures.rs

-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
#![feature(fn_traits)]
1717
#![feature(unboxed_closures)]
18-
#![feature(cfg_sanitize)]
1918

2019
fn foo<'a, T>() -> Box<dyn Fn(&'a T) -> &'a T> {
2120
Box::new(|x| x)
@@ -72,9 +71,6 @@ fn use_closure<C>(call: extern "rust-call" fn(&C, ()) -> i32, f: &C) -> i32 {
7271
}
7372

7473
#[test]
75-
// FIXME after KCFI reify support is added, remove this
76-
// It will appear to work if you test locally, set -C opt-level=0 to see it fail.
77-
#[cfg_attr(sanitize = "kcfi", ignore)]
7874
fn closure_addr_taken() {
7975
let x = 3i32;
8076
let f = || x;

tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs

+38-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
11
// Verifies that casting a method to a function pointer works.
2-
//
3-
// FIXME(#122848): Remove only-linux when fixed.
2+
3+
//@ revisions: cfi kcfi
4+
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
45
//@ only-linux
5-
//@ needs-sanitizer-cfi
6-
//@ compile-flags: -Clto -Copt-level=0 -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi
6+
//@ [cfi] needs-sanitizer-cfi
7+
//@ [kcfi] needs-sanitizer-kcfi
8+
//@ compile-flags: -C target-feature=-crt-static
9+
//@ [cfi] compile-flags: -C opt-level=0 -C codegen-units=1 -C lto
10+
//@ [cfi] compile-flags: -C prefer-dynamic=off
11+
//@ [cfi] compile-flags: -Z sanitizer=cfi
12+
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
13+
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
714
//@ run-pass
815

16+
trait Foo {
17+
fn foo(&self);
18+
fn bar(&self);
19+
}
20+
21+
struct S;
22+
23+
impl Foo for S {
24+
fn foo(&self) {}
25+
#[track_caller]
26+
fn bar(&self) {}
27+
}
28+
29+
struct S2 {
30+
f: fn(&S)
31+
}
32+
33+
impl S2 {
34+
fn foo(&self, s: &S) {
35+
(self.f)(s)
36+
}
37+
}
38+
939
trait Trait1 {
1040
fn foo(&self);
1141
}
@@ -20,4 +50,8 @@ fn main() {
2050
let type1 = Type1 {};
2151
let f = <Type1 as Trait1>::foo;
2252
f(&type1);
53+
// Check again with different optimization barriers
54+
S2 { f: <S as Foo>::foo }.foo(&S);
55+
// Check mismatched #[track_caller]
56+
S2 { f: <S as Foo>::bar }.foo(&S)
2357
}

0 commit comments

Comments
 (0)