1
Fork 0

Add LLVM KCFI support to the Rust compiler

This commit adds LLVM Kernel Control Flow Integrity (KCFI) support to
the Rust compiler. It initially provides forward-edge control flow
protection for operating systems kernels for Rust-compiled code only by
aggregating function pointers in groups identified by their return and
parameter types. (See llvm/llvm-project@cff5bef.)

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) will be provided in later work as
part of this project by identifying C char and integer type uses at the
time types are encoded (see Type metadata in the design document in the
tracking issue #89653).

LLVM KCFI can be enabled with -Zsanitizer=kcfi.

Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com>
This commit is contained in:
Ramon de C Valle 2022-11-21 21:29:00 -08:00
parent b7bc90fea3
commit 65698ae9f3
26 changed files with 231 additions and 28 deletions

View file

@ -88,7 +88,8 @@ pub(crate) unsafe fn codegen(
callee,
args.as_ptr(),
args.len() as c_uint,
None,
[].as_ptr(),
0 as c_uint,
);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
@ -132,8 +133,15 @@ pub(crate) unsafe fn codegen(
.enumerate()
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
.collect::<Vec<_>>();
let ret =
llvm::LLVMRustBuildCall(llbuilder, ty, callee, args.as_ptr(), args.len() as c_uint, None);
let ret = llvm::LLVMRustBuildCall(
llbuilder,
ty,
callee,
args.as_ptr(),
args.len() as c_uint,
[].as_ptr(),
0 as c_uint,
);
llvm::LLVMSetTailCall(ret, True);
llvm::LLVMBuildRetVoid(llbuilder);
llvm::LLVMDisposeBuilder(llbuilder);

View file

@ -20,6 +20,7 @@ use rustc_middle::ty::layout::{
};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
use rustc_symbol_mangling::typeid::kcfi_typeid_for_fnabi;
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
use rustc_target::spec::{HasTargetSpec, Target};
use std::borrow::Cow;
@ -225,9 +226,25 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
debug!("invoke {:?} with args ({:?})", llfn, args);
let args = self.check_call("invoke", llty, llfn, args);
let bundle = funclet.map(|funclet| funclet.bundle());
let bundle = bundle.as_ref().map(|b| &*b.raw);
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
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() && fn_abi.is_some() && is_indirect_call {
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap());
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);
}
bundles.retain(|bundle| bundle.is_some());
let invoke = unsafe {
llvm::LLVMRustBuildInvoke(
self.llbuilder,
@ -237,7 +254,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
args.len() as c_uint,
then,
catch,
bundle,
bundles.as_ptr(),
bundles.len() as c_uint,
UNNAMED,
)
};
@ -1143,7 +1161,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
llfn,
args.as_ptr() as *const &llvm::Value,
args.len() as c_uint,
None,
[].as_ptr(),
0 as c_uint,
);
}
}
@ -1159,9 +1178,25 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
debug!("call {:?} with args ({:?})", llfn, args);
let args = self.check_call("call", llty, llfn, args);
let bundle = funclet.map(|funclet| funclet.bundle());
let bundle = bundle.as_ref().map(|b| &*b.raw);
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
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() && fn_abi.is_some() && is_indirect_call {
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap());
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);
}
bundles.retain(|bundle| bundle.is_some());
let call = unsafe {
llvm::LLVMRustBuildCall(
self.llbuilder,
@ -1169,7 +1204,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
llfn,
args.as_ptr() as *const &llvm::Value,
args.len() as c_uint,
bundle,
bundles.as_ptr(),
bundles.len() as c_uint,
)
};
if let Some(fn_abi) = fn_abi {

View file

@ -250,6 +250,11 @@ pub unsafe fn create_module<'ll>(
);
}
if sess.is_sanitizer_kcfi_enabled() {
let kcfi = "kcfi\0".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
}
// Control Flow Guard is currently only supported by the MSVC linker on Windows.
if sess.target.is_like_msvc {
match sess.opts.cg.control_flow_guard {

View file

@ -20,7 +20,7 @@ use crate::type_::Type;
use crate::value::Value;
use rustc_codegen_ssa::traits::TypeMembershipMethods;
use rustc_middle::ty::Ty;
use rustc_symbol_mangling::typeid::typeid_for_fnabi;
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi};
use smallvec::SmallVec;
/// Declare a function.
@ -136,6 +136,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
self.set_type_metadata(llfn, typeid);
}
if self.tcx.sess.is_sanitizer_kcfi_enabled() {
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
self.set_kcfi_type_metadata(llfn, kcfi_typeid);
}
llfn
}

View file

@ -427,6 +427,7 @@ pub enum MetadataType {
MD_type = 19,
MD_vcall_visibility = 28,
MD_noundef = 29,
MD_kcfi_type = 36,
}
/// LLVMRustAsmDialect
@ -1060,6 +1061,7 @@ extern "C" {
pub fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
pub fn LLVMRustGlobalAddMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
pub fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
pub fn LLVMIsAFunction(Val: &Value) -> Option<&Value>;
// Operations on constants of any type
pub fn LLVMConstNull(Ty: &Type) -> &Value;
@ -1270,7 +1272,8 @@ extern "C" {
NumArgs: c_uint,
Then: &'a BasicBlock,
Catch: &'a BasicBlock,
Bundle: Option<&OperandBundleDef<'a>>,
OpBundles: *const Option<&OperandBundleDef<'a>>,
NumOpBundles: c_uint,
Name: *const c_char,
) -> &'a Value;
pub fn LLVMBuildLandingPad<'a>(
@ -1640,7 +1643,8 @@ extern "C" {
Fn: &'a Value,
Args: *const &'a Value,
NumArgs: c_uint,
Bundle: Option<&OperandBundleDef<'a>>,
OpBundles: *const Option<&OperandBundleDef<'a>>,
NumOpBundles: c_uint,
) -> &'a Value;
pub fn LLVMRustBuildMemCpy<'a>(
B: &Builder<'a>,

View file

@ -316,4 +316,19 @@ impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
)
}
}
fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
let kcfi_type_metadata = self.const_u32(kcfi_typeid);
unsafe {
llvm::LLVMGlobalSetMetadata(
function,
llvm::MD_kcfi_type as c_uint,
llvm::LLVMMDNodeInContext2(
self.llcx,
&llvm::LLVMValueAsMetadata(kcfi_type_metadata),
1,
),
)
}
}
}