Add cross-language LLVM CFI support to the Rust compiler
This commit adds cross-language LLVM Control Flow Integrity (CFI) support to the Rust compiler by adding the `-Zsanitizer-cfi-normalize-integers` option to be used with Clang `-fsanitize-cfi-icall-normalize-integers` for normalizing integer types (see https://reviews.llvm.org/D139395). It provides forward-edge control flow protection for C or C++ and Rust -compiled code "mixed binaries" (i.e., for when C or C++ and Rust -compiled code share the same virtual address space). For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, see design document in the tracking issue #89653. Cross-language LLVM CFI can be enabled with -Zsanitizer=cfi and -Zsanitizer-cfi-normalize-integers, and requires proper (i.e., non-rustc) LTO (i.e., -Clinker-plugin-lto).
This commit is contained in:
@@ -15,14 +15,15 @@ use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_data_structures::small_c_str::SmallCStr;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::ty::layout::{
|
||||
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_symbol_mangling::typeid::kcfi_typeid_for_fnabi;
|
||||
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions};
|
||||
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
|
||||
use rustc_target::spec::{HasTargetSpec, Target};
|
||||
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CStr;
|
||||
use std::iter;
|
||||
@@ -216,6 +217,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
fn invoke(
|
||||
&mut self,
|
||||
llty: &'ll Type,
|
||||
fn_attrs: Option<&CodegenFnAttrs>,
|
||||
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
|
||||
llfn: &'ll Value,
|
||||
args: &[&'ll Value],
|
||||
@@ -230,19 +232,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
|
||||
let mut bundles = vec![funclet_bundle];
|
||||
|
||||
// Set KCFI operand bundle
|
||||
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
|
||||
let kcfi_bundle =
|
||||
if self.tcx.sess.is_sanitizer_kcfi_enabled() && let Some(fn_abi) = fn_abi && is_indirect_call {
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
|
||||
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if kcfi_bundle.is_some() {
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
}
|
||||
// Emit CFI pointer type membership test
|
||||
self.cfi_type_test(fn_attrs, fn_abi, llfn);
|
||||
|
||||
// Emit KCFI operand bundle
|
||||
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
|
||||
bundles.retain(|bundle| bundle.is_some());
|
||||
let invoke = unsafe {
|
||||
@@ -1183,6 +1179,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
fn call(
|
||||
&mut self,
|
||||
llty: &'ll Type,
|
||||
fn_attrs: Option<&CodegenFnAttrs>,
|
||||
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
|
||||
llfn: &'ll Value,
|
||||
args: &[&'ll Value],
|
||||
@@ -1195,19 +1192,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
|
||||
let mut bundles = vec![funclet_bundle];
|
||||
|
||||
// Set KCFI operand bundle
|
||||
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
|
||||
let kcfi_bundle =
|
||||
if let Some(fn_abi) = fn_abi && self.tcx.sess.is_sanitizer_kcfi_enabled() && is_indirect_call {
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
|
||||
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if kcfi_bundle.is_some() {
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
}
|
||||
// Emit CFI pointer type membership test
|
||||
self.cfi_type_test(fn_attrs, fn_abi, llfn);
|
||||
|
||||
// Emit KCFI operand bundle
|
||||
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
|
||||
bundles.retain(|bundle| bundle.is_some());
|
||||
let call = unsafe {
|
||||
@@ -1456,7 +1447,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
|
||||
pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value {
|
||||
let (ty, f) = self.cx.get_intrinsic(intrinsic);
|
||||
self.call(ty, None, f, args, None)
|
||||
self.call(ty, None, None, f, args, None)
|
||||
}
|
||||
|
||||
fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) {
|
||||
@@ -1518,7 +1509,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width)
|
||||
};
|
||||
let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
|
||||
self.call(self.type_func(&[src_ty], dest_ty), None, f, &[val], None)
|
||||
self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None)
|
||||
}
|
||||
|
||||
pub(crate) fn landing_pad(
|
||||
@@ -1535,4 +1526,71 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
llvm::LLVMBuildLandingPad(self.llbuilder, ty, None, num_clauses as c_uint, UNNAMED)
|
||||
}
|
||||
}
|
||||
|
||||
// Emits CFI pointer type membership tests.
|
||||
fn cfi_type_test(
|
||||
&mut self,
|
||||
fn_attrs: Option<&CodegenFnAttrs>,
|
||||
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
|
||||
llfn: &'ll Value,
|
||||
) {
|
||||
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
|
||||
if is_indirect_call && fn_abi.is_some() && self.tcx.sess.is_sanitizer_cfi_enabled() {
|
||||
if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::CFI) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut options = TypeIdOptions::empty();
|
||||
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
|
||||
options.insert(TypeIdOptions::GENERALIZE_POINTERS);
|
||||
}
|
||||
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
|
||||
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
|
||||
}
|
||||
|
||||
let typeid = typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
|
||||
let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
|
||||
|
||||
// Test whether the function pointer is associated with the type identifier.
|
||||
let cond = self.type_test(llfn, typeid_metadata);
|
||||
let bb_pass = self.append_sibling_block("type_test.pass");
|
||||
let bb_fail = self.append_sibling_block("type_test.fail");
|
||||
self.cond_br(cond, bb_pass, bb_fail);
|
||||
|
||||
self.switch_to_block(bb_fail);
|
||||
self.abort();
|
||||
self.unreachable();
|
||||
|
||||
self.switch_to_block(bb_pass);
|
||||
}
|
||||
}
|
||||
|
||||
// Emits KCFI operand bundles.
|
||||
fn kcfi_operand_bundle(
|
||||
&mut self,
|
||||
fn_attrs: Option<&CodegenFnAttrs>,
|
||||
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
|
||||
llfn: &'ll Value,
|
||||
) -> Option<llvm::OperandBundleDef<'ll>> {
|
||||
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
|
||||
let kcfi_bundle = if is_indirect_call && self.tcx.sess.is_sanitizer_kcfi_enabled() {
|
||||
if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::KCFI) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut options = TypeIdOptions::empty();
|
||||
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
|
||||
options.insert(TypeIdOptions::GENERALIZE_POINTERS);
|
||||
}
|
||||
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
|
||||
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
|
||||
}
|
||||
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
|
||||
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
kcfi_bundle
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user