Skip to content

Commit

Permalink
CFI: Strip auto traits off Self for virtual calls
Browse files Browse the repository at this point in the history
Additional trait bounds beyond the principal trait and its implications
are not possible in the vtable. This means that if a receiver is
`&dyn Foo + Send`, the function will only be expecting `&dyn Foo`.

This strips those auto traits off before CFI encoding.
  • Loading branch information
maurer committed Mar 22, 2024
1 parent 90e363a commit 042e05b
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 4 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_symbol_mangling/src/typeid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fn typeid_for_instance<'tcx>(
instance: &Instance<'tcx>,
options: TypeIdOptions,
) -> String {
typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options)
typeid_itanium_cxx_abi::typeid_for_instance(tcx, *instance, options)
}

/// Returns a KCFI type metadata identifier for the specified FnAbi.
Expand All @@ -61,6 +61,6 @@ pub fn kcfi_typeid_for_instance<'tcx>(
// A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the
// xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
let mut hash: XxHash64 = Default::default();
hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes());
hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, *instance, options).as_bytes());
hash.finish() as u32
}
Original file line number Diff line number Diff line change
Expand Up @@ -1087,11 +1087,15 @@ pub fn typeid_for_fnabi<'tcx>(
/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary.
pub fn typeid_for_instance<'tcx>(
tcx: TyCtxt<'tcx>,
instance: &Instance<'tcx>,
mut instance: Instance<'tcx>,
options: TypeIdOptions,
) -> String {
if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
instance.args = strip_receiver_auto(tcx, instance.args)
}

let fn_abi = tcx
.fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty())))
.fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty())))
.unwrap_or_else(|instance| {
bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance)
});
Expand Down Expand Up @@ -1137,3 +1141,23 @@ pub fn typeid_for_instance<'tcx>(

typeid_for_fnabi(tcx, fn_abi, options)
}

fn strip_receiver_auto<'tcx>(
tcx: TyCtxt<'tcx>,
args: ty::GenericArgsRef<'tcx>,
) -> ty::GenericArgsRef<'tcx> {
let ty = args.type_at(0);
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
bug!("Tried to strip auto traits from non-dynamic type {ty}");
};
let filtered_preds =
if preds.principal().is_some() {
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
}))
} else {
ty::List::empty()
};
let new_rcvr = Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind);
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
}
22 changes: 22 additions & 0 deletions tests/ui/sanitizer/cfi-virtual-auto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Tests that calling a trait object method on a trait object with additional auto traits works.

//@ needs-sanitizer-cfi
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
//@ only-linux
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0
//@ run-pass

trait Foo {
fn foo(&self);
}

struct Bar;
impl Foo for Bar {
fn foo(&self) {}
}

pub fn main() {
let x: &(dyn Foo + Send) = &Bar;
x.foo();
}

0 comments on commit 042e05b

Please sign in to comment.