about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthew Maurer <mmaurer@google.com>2024-03-15 18:44:40 +0000
committerMatthew Maurer <mmaurer@google.com>2024-03-23 18:30:45 +0000
commitf434c27067085d72c7770da3b6d4e0bc316fd267 (patch)
treede6904f03fafbbff57d753505e7f3df126b2c87b
parent7967915c7b456d59fd963260822d3cf583969948 (diff)
downloadrust-f434c27067085d72c7770da3b6d4e0bc316fd267.tar.gz
rust-f434c27067085d72c7770da3b6d4e0bc316fd267.zip
CFI: Strip auto traits off Self for virtual calls
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.
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid.rs4
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs28
-rw-r--r--tests/ui/sanitizer/cfi-virtual-auto.rs22
3 files changed, 50 insertions, 4 deletions
diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs
index e8763e49e62..3bf564a4a16 100644
--- a/compiler/rustc_symbol_mangling/src/typeid.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid.rs
@@ -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.
@@ -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
 }
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
index 07a382d161d..813d6419955 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -1088,11 +1088,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)
         });
@@ -1138,3 +1142,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))
+}
diff --git a/tests/ui/sanitizer/cfi-virtual-auto.rs b/tests/ui/sanitizer/cfi-virtual-auto.rs
new file mode 100644
index 00000000000..7a0c246a412
--- /dev/null
+++ b/tests/ui/sanitizer/cfi-virtual-auto.rs
@@ -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();
+}