Skip to content

Commit 7a26eff

Browse files
authored
Rollup merge of rust-lang#144936 - rcvalle:rust-cfi-fix-144641, r=lcnr
CFI: Fix types that implement Fn, FnMut, or FnOnce 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. Fixes rust-lang#144641. cc `@1c3t3a` `@Jakob-Koschel`
2 parents adaa838 + 20d3d57 commit 7a26eff

File tree

3 files changed

+93
-5
lines changed

3 files changed

+93
-5
lines changed

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

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ use rustc_hir as hir;
1010
use rustc_hir::LangItem;
1111
use rustc_middle::bug;
1212
use rustc_middle::ty::{
13-
self, AssocContainer, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy,
14-
List, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
15-
UintTy,
13+
self, AssocContainer, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, TraitRef,
14+
Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy,
1615
};
1716
use rustc_span::def_id::DefId;
1817
use rustc_span::{DUMMY_SP, sym};
@@ -459,6 +458,30 @@ pub(crate) fn transform_instance<'tcx>(
459458
instance
460459
}
461460

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+
///
462485
fn implemented_method<'tcx>(
463486
tcx: TyCtxt<'tcx>,
464487
instance: Instance<'tcx>,
@@ -476,10 +499,11 @@ fn implemented_method<'tcx>(
476499
trait_id = trait_ref.skip_binder().def_id;
477500
impl_id
478501
} else if let AssocContainer::Trait = assoc.container
479-
&& let InstanceKind::Item(def_id) = instance.def
502+
&& let Some(trait_method_def_id) = default_or_shim(tcx, instance)
480503
{
504+
// Provided method in a `trait` block or a synthetic `shim`
481505
trait_method = assoc;
482-
method_id = def_id;
506+
method_id = trait_method_def_id;
483507
trait_id = tcx.parent(method_id);
484508
trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args));
485509
trait_id
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-kcfi
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)