Skip to content

Commit

Permalink
CFI: Support function pointers for trait methods
Browse files Browse the repository at this point in the history
Adds support for both CFI and KCFI for attaching concrete and abstract
types to functions. KCFI does this through generation of `ReifyShim` on
any function pointer that could go in a vtable, and checking the
`ReifyReason` when emitting the instance. CFI does this by attaching
both the concrete and abstract type to every instance.

Fixes #115953
  • Loading branch information
maurer committed Mar 25, 2024
1 parent 2292393 commit f388dab
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 15 deletions.
25 changes: 13 additions & 12 deletions compiler/rustc_codegen_llvm/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::llvm;
use crate::llvm::AttributePlace::Function;
use crate::type_::Type;
use crate::value::Value;
use itertools::Itertools;
use rustc_codegen_ssa::traits::TypeMembershipMethods;
use rustc_middle::ty::{Instance, Ty};
use rustc_symbol_mangling::typeid::{
Expand Down Expand Up @@ -143,18 +144,18 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
if let Some(instance) = instance {
let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty());
self.set_type_metadata(llfn, typeid);
let typeid =
typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS);
self.add_type_metadata(llfn, typeid);
let typeid =
typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS);
self.add_type_metadata(llfn, typeid);
let typeid = typeid_for_instance(
self.tcx,
&instance,
TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS,
);
self.add_type_metadata(llfn, typeid);
for options in [
TypeIdOptions::GENERALIZE_POINTERS,
TypeIdOptions::NORMALIZE_INTEGERS,
TypeIdOptions::CONCRETE,
]
.into_iter()
.powerset()
.map(TypeIdOptions::from_iter)
{
let typeid = typeid_for_instance(self.tcx, &instance, options);
self.add_type_metadata(llfn, typeid);
}
} else {
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty());
self.set_type_metadata(llfn, typeid);
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,14 @@ impl<'tcx> Instance<'tcx> {
debug!(" => fn pointer created for virtual call");
resolved.def = InstanceDef::ReifyShim(def_id, reason);
}
// FIXME(maurer) only shim it if it is a vtable-safe function
_ if tcx.sess.is_sanitizer_kcfi_enabled()
&& tcx.associated_item(def_id).trait_item_def_id.is_some() =>
{
// If this function could also go in a vtable, we need to `ReifyShim` it with
// KCFI because it can only attach one type per function.
resolved.def = InstanceDef::ReifyShim(resolved.def_id(), reason)
}
_ => {}
}

Expand Down
11 changes: 9 additions & 2 deletions compiler/rustc_symbol_mangling/src/typeid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
/// see design document in the tracking issue #89653.
use bitflags::bitflags;
use rustc_middle::ty::{Instance, Ty, TyCtxt};
use rustc_middle::ty::{Instance, InstanceDef, Ty, TyCtxt};
use rustc_target::abi::call::FnAbi;
use std::hash::Hasher;
use twox_hash::XxHash64;
Expand All @@ -16,6 +16,8 @@ bitflags! {
const GENERALIZE_POINTERS = 1;
const GENERALIZE_REPR_C = 2;
const NORMALIZE_INTEGERS = 4;
// Only relevant for `typeid_for_instance`
const CONCRETE = 8;
}
}

Expand Down Expand Up @@ -56,8 +58,13 @@ pub fn kcfi_typeid_for_fnabi<'tcx>(
pub fn kcfi_typeid_for_instance<'tcx>(
tcx: TyCtxt<'tcx>,
instance: &Instance<'tcx>,
options: TypeIdOptions,
mut options: TypeIdOptions,
) -> u32 {
// If we receive a `ReifyShim` intended to produce a function pointer, we need to remain
// concrete - abstraction is for vtables.
if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) {
options |= TypeIdOptions::CONCRETE
}
// 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,8 @@ pub fn typeid_for_instance<'tcx>(
instance.args = strip_receiver_auto(tcx, instance.args)
}

if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
if !options.contains(TypeIdOptions::CONCRETE)
&& let Some(impl_id) = tcx.impl_of_method(instance.def_id())
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
{
let impl_method = tcx.associated_item(instance.def_id());
Expand Down

0 comments on commit f388dab

Please sign in to comment.