1
Fork 0

Add LLVM CFI support to the Rust compiler

This commit adds LLVM Control Flow Integrity (CFI) support to the Rust
compiler. It initially provides forward-edge control flow protection for
Rust-compiled code only by aggregating function pointers in groups
identified by their number of arguments.

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 defining and using compatible type identifiers
(see Type metadata in the design document in the tracking issue #89653).

LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e.,
-Clto).
This commit is contained in:
Ramon de C Valle 2021-10-07 15:33:13 -07:00
parent 1067e2ca5e
commit 5d30e93189
33 changed files with 271 additions and 20 deletions

View file

@ -19,6 +19,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
use rustc_span::source_map::Span;
use rustc_span::{sym, Symbol};
use rustc_symbol_mangling::typeid_for_fnabi;
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
use rustc_target::spec::abi::Abi;
@ -818,12 +819,43 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.codegen_argument(&mut bx, location, &mut llargs, last_arg);
}
let fn_ptr = match (llfn, instance) {
(Some(llfn), _) => llfn,
(None, Some(instance)) => bx.get_fn_addr(instance),
let (is_indirect_call, fn_ptr) = match (llfn, instance) {
(Some(llfn), _) => (true, llfn),
(None, Some(instance)) => (false, bx.get_fn_addr(instance)),
_ => span_bug!(span, "no llfn for call"),
};
// For backends that support CFI using type membership (i.e., testing whether a given
// pointer is associated with a type identifier).
if bx.tcx().sess.is_sanitizer_cfi_enabled() && is_indirect_call {
// Emit type metadata and checks.
// FIXME(rcvalle): Add support for generalized identifiers.
// FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers.
let typeid = typeid_for_fnabi(bx.tcx(), fn_abi);
let typeid_metadata = bx.typeid_metadata(typeid.clone());
// Test whether the function pointer is associated with the type identifier.
let cond = bx.type_test(fn_ptr, typeid_metadata);
let mut bx_pass = bx.build_sibling_block("type_test.pass");
let mut bx_fail = bx.build_sibling_block("type_test.fail");
bx.cond_br(cond, bx_pass.llbb(), bx_fail.llbb());
helper.do_call(
self,
&mut bx_pass,
fn_abi,
fn_ptr,
&llargs,
destination.as_ref().map(|&(_, target)| (ret_dest, target)),
cleanup,
);
bx_fail.abort();
bx_fail.unreachable();
return;
}
helper.do_call(
self,
&mut bx,

View file

@ -4,6 +4,7 @@ use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
use rustc_symbol_mangling::typeid_for_fnabi;
use rustc_target::abi::call::{FnAbi, PassMode};
use std::iter;
@ -244,6 +245,13 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
for (bb, _) in traversal::reverse_postorder(&mir) {
fx.codegen_block(bb);
}
// For backends that support CFI using type membership (i.e., testing whether a given pointer
// is associated with a type identifier).
if cx.tcx().sess.is_sanitizer_cfi_enabled() {
let typeid = typeid_for_fnabi(cx.tcx(), fn_abi);
bx.type_metadata(llfn, typeid.clone());
}
}
/// Produces, for each argument, a `Value` pointing at the

View file

@ -158,6 +158,8 @@ pub trait BuilderMethods<'a, 'tcx>:
fn range_metadata(&mut self, load: Self::Value, range: WrappingRange);
fn nonnull_metadata(&mut self, load: Self::Value);
fn type_metadata(&mut self, function: Self::Function, typeid: String);
fn typeid_metadata(&mut self, typeid: String) -> Self::Value;
fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value;
fn store_with_flags(

View file

@ -24,6 +24,8 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
///
/// Currently has any effect only when LLVM versions prior to 12.0 are used as the backend.
fn sideeffect(&mut self);
/// Trait method used to test whether a given pointer is associated with a type identifier.
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value;
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
/// Rust defined C-variadic functions.
fn va_start(&mut self, val: Self::Value) -> Self::Value;