Skip to content

Commit

Permalink
CFI: Fix drop and drop_in_place
Browse files Browse the repository at this point in the history
Fix drop and drop_in_place by transforming self of drop and
drop_in_place methods into a Drop trait objects for types that have a
non-empty DropGlue.
  • Loading branch information
rcvalle committed Mar 26, 2024
1 parent c98ea0d commit a676408
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,19 @@ fn encode_ty<'tcx>(
typeid
}

/// Creates a trait object.
#[inline]
fn new_dynamic_trait<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> Ty<'tcx> {
let predicate =
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id: def_id, args: args });
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn)
}

/// Transforms predicates for being encoded and used in the substitution dictionary.
fn transform_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
Expand Down Expand Up @@ -1112,8 +1125,48 @@ pub fn typeid_for_instance<'tcx>(
mut instance: Instance<'tcx>,
options: TypeIdOptions,
) -> String {
if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
instance.args = strip_receiver_auto(tcx, instance.args)
if (matches!(instance.def, ty::InstanceDef::Virtual(..))
&& instance.def_id() == tcx.lang_items().drop_in_place_fn().unwrap())
|| matches!(instance.def, ty::InstanceDef::DropGlue(_, Some(_ty)))
{
// Adjust the type ids of DropGlues
//
// DropGlues may have indirect calls to one or more given types drop function. Rust allows
// for types to be erased to any trait object and retains the drop function for the original
// type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is
// called a second time, it only has information after type erasure and it could be a call
// on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on
// declaration/definition, and during code generation at call sites so they have the same
// type id and match.
//
// FIXME(rcvalle): This allows a drop call on any trait object of types that have a
// non-empty DropGlue to call the DropGlue of any other types that have a non-empty
// DropGlue.
let fn_abi = tcx
.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)
});
let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() {
Ty::new_mut_ref(
tcx,
tcx.lifetimes.re_erased,
new_dynamic_trait(tcx, tcx.lang_items().drop_trait().unwrap(), List::empty()),
)
} else {
Ty::new_imm_ref(
tcx,
tcx.lifetimes.re_erased,
new_dynamic_trait(tcx, tcx.lang_items().drop_trait().unwrap(), List::empty()),
)
};
// HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the
// other fields are never used.
let mut fn_abi = fn_abi.clone();
fn_abi.args[0].layout.ty = self_ty;
return typeid_for_fnabi(tcx, &fn_abi, options);
} else if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
instance.args = strip_receiver_auto(tcx, instance.args);
}

if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/sanitizer/cfi-drop-in-place.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Verifies that drops can be called on arbitrary trait objects.
//
// FIXME(#122848): Remove only-linux when fixed.
//@ only-linux
//@ needs-sanitizer-cfi
//@ compile-flags: -Clto -Copt-level=0 -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi
//@ run-pass

struct CustomDrop;
impl Drop for CustomDrop {
fn drop(&mut self) {}
}

fn main() {
let _ = Box::new(CustomDrop) as Box<dyn Send>;
}

0 comments on commit a676408

Please sign in to comment.