Skip to content

Commit 8b21653

Browse files
committed
CFI: Fix types that implement Fn, FnMut, or FnOnce
Fix type transformation for types that implement Fn, FnMut, or FnOnce traits (such as Box<dyn Fn()>, Box<dyn FnMut()>, etc.) by transforming self into a trait object of the trait that defines the method. Fixes #144641.
1 parent e466296 commit 8b21653

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,28 @@ pub(crate) fn transform_instance<'tcx>(
453453
instance.def = ty::InstanceKind::Virtual(call, 0);
454454
instance.args = abstract_args;
455455
}
456+
let fn_traits = [
457+
(LangItem::Fn, sym::call),
458+
(LangItem::FnMut, sym::call_mut),
459+
(LangItem::FnOnce, sym::call_once),
460+
];
461+
for (lang_item, method_sym) in fn_traits {
462+
if let Some(trait_id) = tcx.lang_items().get(lang_item) {
463+
let items = tcx.associated_items(trait_id);
464+
if let Some(call_method) = items.in_definition_order().find(|item| item.name() == method_sym) {
465+
if instance.def_id() == call_method.def_id {
466+
// This is a call to a method of Fn, FnMut, or FnOnce. Transform self into a
467+
// trait object of the trait that defines the method.
468+
let self_ty = trait_object_ty(
469+
tcx,
470+
ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
471+
);
472+
instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1));
473+
break;
474+
}
475+
}
476+
}
477+
}
456478
}
457479

458480
instance
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
//@ compile-flags: -Ctarget-feature=-crt-static -Ccodegen-units=1 -Clto -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=cfi --test
6+
//@ run-pass
7+
8+
#![feature(fn_traits)]
9+
#![feature(unboxed_closures)]
10+
11+
fn foo(_a: u32) {}
12+
13+
#[test]
14+
fn test_fn_trait() {
15+
let f: Box<dyn Fn(u32)> = Box::new(foo);
16+
Fn::call(&f, (0,));
17+
}
18+
19+
#[test]
20+
fn test_fnmut_trait() {
21+
let mut a = 0;
22+
let mut f: Box<dyn FnMut(u32)> = Box::new(|x| a += x);
23+
FnMut::call_mut(&mut f, (1,));
24+
}
25+
26+
#[test]
27+
fn test_fnonce_trait() {
28+
let f: Box<dyn FnOnce(u32)> = Box::new(foo);
29+
FnOnce::call_once(f, (2,));
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
//@ compile-flags: -Ctarget-feature=-crt-static -Zpanic_abort_tests -Cpanic=abort -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=kcfi --test
6+
//@ run-pass
7+
8+
#![feature(fn_traits)]
9+
#![feature(unboxed_closures)]
10+
11+
fn foo(_a: u32) {}
12+
13+
#[test]
14+
fn test_fn_trait() {
15+
let f: Box<dyn Fn(u32)> = Box::new(foo);
16+
Fn::call(&f, (0,));
17+
}
18+
19+
#[test]
20+
fn test_fnmut_trait() {
21+
let mut a = 0;
22+
let mut f: Box<dyn FnMut(u32)> = Box::new(|x| a += x);
23+
FnMut::call_mut(&mut f, (1,));
24+
}
25+
26+
#[test]
27+
fn test_fnonce_trait() {
28+
let f: Box<dyn FnOnce(u32)> = Box::new(foo);
29+
FnOnce::call_once(f, (2,));
30+
}

0 commit comments

Comments
 (0)