Skip to content

Commit bb63853

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 0060d5a commit bb63853

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,35 @@ 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) =
465+
items.in_definition_order().find(|item| item.name() == method_sym)
466+
{
467+
if instance.def_id() == call_method.def_id {
468+
// This is a call to a method of Fn, FnMut, or FnOnce. Transform self into a
469+
// trait object of the trait that defines the method.
470+
let self_ty = trait_object_ty(
471+
tcx,
472+
ty::Binder::dummy(ty::TraitRef::from_method(
473+
tcx,
474+
trait_id,
475+
instance.args,
476+
)),
477+
);
478+
instance.args =
479+
tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1));
480+
break;
481+
}
482+
}
483+
}
484+
}
456485
}
457486

458487
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)