Skip to content

Commit 3821bac

Browse files
committed
CFI: Rewrite FnPtrShim when generalizing
When looking for instances which could either be dynamically called through a vtable or through a concrete trait method, we missed `FnPtrShim`, instead only looking at `Item` and closure-likes.
1 parent 0060d5a commit 3821bac

File tree

3 files changed

+93
-7
lines changed

3 files changed

+93
-7
lines changed

compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use rustc_hir as hir;
1010
use rustc_hir::LangItem;
1111
use rustc_middle::bug;
1212
use rustc_middle::ty::{
13-
self, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy, List, TraitRef, Ty,
14-
TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy,
13+
self, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, TraitRef, Ty, TyCtxt,
14+
TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy,
1515
};
1616
use rustc_span::def_id::DefId;
1717
use rustc_span::{DUMMY_SP, sym};
@@ -458,6 +458,30 @@ pub(crate) fn transform_instance<'tcx>(
458458
instance
459459
}
460460

461+
fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option<DefId> {
462+
match instance.def {
463+
ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => {
464+
tcx.opt_associated_item(def_id).map(|item| item.def_id)
465+
}
466+
_ => None,
467+
}
468+
}
469+
470+
/// Determines if an instance represents a trait method implementation and returns the necessary
471+
/// information for type erasure.
472+
///
473+
/// This function handles two main cases:
474+
///
475+
/// * **Implementation in an `impl` block**: When the instance represents a concrete implementation
476+
/// of a trait method in an `impl` block, it extracts the trait reference, method ID, and trait
477+
/// ID from the implementation. The method ID is obtained from the `trait_item_def_id` field of
478+
/// the associated item, which points to the original trait method definition.
479+
///
480+
/// * **Provided method in a `trait` block or synthetic `shim`**: When the instance represents a
481+
/// default implementation provided in the trait definition itself or a synthetic shim, it uses
482+
/// the instance's own `def_id` as the method ID and determines the trait ID from the associated
483+
/// item.
484+
///
461485
fn implemented_method<'tcx>(
462486
tcx: TyCtxt<'tcx>,
463487
instance: Instance<'tcx>,
@@ -474,11 +498,9 @@ fn implemented_method<'tcx>(
474498
trait_method = tcx.associated_item(method_id);
475499
trait_id = trait_ref.skip_binder().def_id;
476500
impl_id
477-
} else if let InstanceKind::Item(def_id) = instance.def
478-
&& let Some(trait_method_bound) = tcx.opt_associated_item(def_id)
479-
{
480-
// Provided method in a `trait` block
481-
trait_method = trait_method_bound;
501+
} else if let Some(trait_method_def_id) = default_or_shim(tcx, instance) {
502+
// Provided method in a `trait` block or a synthetic `shim`
503+
trait_method = tcx.associated_item(trait_method_def_id);
482504
method_id = instance.def_id();
483505
trait_id = tcx.trait_of_assoc(method_id)?;
484506
trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Verifies that types that implement the Fn, FnMut, or FnOnce traits can be
2+
// called through their trait methods.
3+
//
4+
//@ needs-sanitizer-cfi
5+
//@ only-linux
6+
//@ ignore-backends: gcc
7+
//@ compile-flags: -Ctarget-feature=-crt-static -Ccodegen-units=1 -Clto -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer --test
8+
//@ run-pass
9+
10+
#![feature(fn_traits)]
11+
#![feature(unboxed_closures)]
12+
13+
fn foo(_a: u32) {}
14+
15+
#[test]
16+
fn test_fn_trait() {
17+
let f: Box<dyn Fn(u32)> = Box::new(foo);
18+
Fn::call(&f, (0,));
19+
}
20+
21+
#[test]
22+
fn test_fnmut_trait() {
23+
let mut a = 0;
24+
let mut f: Box<dyn FnMut(u32)> = Box::new(|x| a += x);
25+
FnMut::call_mut(&mut f, (1,));
26+
}
27+
28+
#[test]
29+
fn test_fnonce_trait() {
30+
let f: Box<dyn FnOnce(u32)> = Box::new(foo);
31+
FnOnce::call_once(f, (2,));
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Verifies that types that implement the Fn, FnMut, or FnOnce traits can be
2+
// called through their trait methods.
3+
//
4+
//@ needs-sanitizer-cfi
5+
//@ only-linux
6+
//@ ignore-backends: gcc
7+
//@ compile-flags: -Ctarget-feature=-crt-static -Zpanic_abort_tests -Cpanic=abort -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=kcfi -Cunsafe-allow-abi-mismatch=sanitizer --test
8+
//@ run-pass
9+
10+
#![feature(fn_traits)]
11+
#![feature(unboxed_closures)]
12+
13+
fn foo(_a: u32) {}
14+
15+
#[test]
16+
fn test_fn_trait() {
17+
let f: Box<dyn Fn(u32)> = Box::new(foo);
18+
Fn::call(&f, (0,));
19+
}
20+
21+
#[test]
22+
fn test_fnmut_trait() {
23+
let mut a = 0;
24+
let mut f: Box<dyn FnMut(u32)> = Box::new(|x| a += x);
25+
FnMut::call_mut(&mut f, (1,));
26+
}
27+
28+
#[test]
29+
fn test_fnonce_trait() {
30+
let f: Box<dyn FnOnce(u32)> = Box::new(foo);
31+
FnOnce::call_once(f, (2,));
32+
}

0 commit comments

Comments
 (0)