Skip to content

Commit a8f6e61

Browse files
committed
Auto merge of #89652 - rcvalle:rust-cfi, r=nagisa
Add LLVM CFI support to the Rust compiler This PR 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). Thank you, `@eddyb` and `@pcc,` for all the help!
2 parents 47aeac6 + c5708ca commit a8f6e61

35 files changed

+473
-39
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,16 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
915915
// TODO(antoyo)
916916
}
917917

918+
fn type_metadata(&mut self, _function: RValue<'gcc>, _typeid: String) {
919+
// Unsupported.
920+
}
921+
922+
fn typeid_metadata(&mut self, _typeid: String) -> RValue<'gcc> {
923+
// Unsupported.
924+
self.context.new_rvalue_from_int(self.int_type, 0)
925+
}
926+
927+
918928
fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
919929
self.store_with_flags(val, ptr, align, MemFlags::empty())
920930
}

compiler/rustc_codegen_gcc/src/intrinsic/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,11 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
367367
// TODO(antoyo)
368368
}
369369

370+
fn type_test(&mut self, _pointer: Self::Value, _typeid: Self::Value) -> Self::Value {
371+
// Unsupported.
372+
self.context.new_rvalue_from_int(self.int_type, 0)
373+
}
374+
370375
fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> {
371376
unimplemented!();
372377
}

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,32 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
604604
}
605605
}
606606

607+
fn type_metadata(&mut self, function: &'ll Value, typeid: String) {
608+
let typeid_metadata = self.typeid_metadata(typeid);
609+
let v = [self.const_usize(0), typeid_metadata];
610+
unsafe {
611+
llvm::LLVMGlobalSetMetadata(
612+
function,
613+
llvm::MD_type as c_uint,
614+
llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
615+
self.cx.llcx,
616+
v.as_ptr(),
617+
v.len() as c_uint,
618+
)),
619+
)
620+
}
621+
}
622+
623+
fn typeid_metadata(&mut self, typeid: String) -> Self::Value {
624+
unsafe {
625+
llvm::LLVMMDStringInContext(
626+
self.cx.llcx,
627+
typeid.as_ptr() as *const c_char,
628+
typeid.as_bytes().len() as c_uint,
629+
)
630+
}
631+
}
632+
607633
fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
608634
self.store_with_flags(val, ptr, align, MemFlags::empty())
609635
}

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@ pub unsafe fn create_module(
221221
llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1);
222222
}
223223

224+
if sess.is_sanitizer_cfi_enabled() {
225+
// FIXME(rcvalle): Add support for non canonical jump tables.
226+
let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast();
227+
// FIXME(rcvalle): Add it with Override behavior flag--LLVMRustAddModuleFlag adds it with
228+
// Warning behavior flag. Add support for specifying the behavior flag to
229+
// LLVMRustAddModuleFlag.
230+
llvm::LLVMRustAddModuleFlag(llmod, canonical_jump_tables, 1);
231+
}
232+
224233
// Control Flow Guard is currently only supported by the MSVC linker on Windows.
225234
if sess.target.is_like_msvc {
226235
match sess.opts.cg.control_flow_guard {
@@ -779,6 +788,8 @@ impl CodegenCx<'b, 'tcx> {
779788
ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void);
780789
}
781790

791+
ifn!("llvm.type.test", fn(i8p, self.type_metadata()) -> i1);
792+
782793
if self.sess().opts.debuginfo != DebugInfo::None {
783794
ifn!("llvm.dbg.declare", fn(self.type_metadata(), self.type_metadata()) -> void);
784795
ifn!("llvm.dbg.value", fn(self.type_metadata(), t_i64, self.type_metadata()) -> void);

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,14 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
401401
}
402402
}
403403

404+
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value {
405+
// Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time
406+
// optimization pass replaces calls to this intrinsic with code to test type membership.
407+
let i8p_ty = self.type_i8p();
408+
let bitcast = self.bitcast(pointer, i8p_ty);
409+
self.call_intrinsic("llvm.type.test", &[bitcast, typeid])
410+
}
411+
404412
fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value {
405413
self.call_intrinsic("llvm.va_start", &[va_list])
406414
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ pub enum MetadataType {
416416
MD_nontemporal = 9,
417417
MD_mem_parallel_loop_access = 10,
418418
MD_nonnull = 11,
419+
MD_type = 19,
419420
}
420421

421422
/// LLVMRustAsmDialect
@@ -1002,6 +1003,8 @@ extern "C" {
10021003
pub fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t);
10031004
pub fn LLVMReplaceAllUsesWith(OldVal: &'a Value, NewVal: &'a Value);
10041005
pub fn LLVMSetMetadata(Val: &'a Value, KindID: c_uint, Node: &'a Value);
1006+
pub fn LLVMGlobalSetMetadata(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
1007+
pub fn LLVMValueAsMetadata(Node: &'a Value) -> &Metadata;
10051008

10061009
// Operations on constants of any type
10071010
pub fn LLVMConstNull(Ty: &Type) -> &Value;

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
1919
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
2020
use rustc_span::source_map::Span;
2121
use rustc_span::{sym, Symbol};
22+
use rustc_symbol_mangling::typeid_for_fnabi;
2223
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
2324
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
2425
use rustc_target::spec::abi::Abi;
@@ -818,12 +819,43 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
818819
self.codegen_argument(&mut bx, location, &mut llargs, last_arg);
819820
}
820821

821-
let fn_ptr = match (llfn, instance) {
822-
(Some(llfn), _) => llfn,
823-
(None, Some(instance)) => bx.get_fn_addr(instance),
822+
let (is_indirect_call, fn_ptr) = match (llfn, instance) {
823+
(Some(llfn), _) => (true, llfn),
824+
(None, Some(instance)) => (false, bx.get_fn_addr(instance)),
824825
_ => span_bug!(span, "no llfn for call"),
825826
};
826827

828+
// For backends that support CFI using type membership (i.e., testing whether a given
829+
// pointer is associated with a type identifier).
830+
if bx.tcx().sess.is_sanitizer_cfi_enabled() && is_indirect_call {
831+
// Emit type metadata and checks.
832+
// FIXME(rcvalle): Add support for generalized identifiers.
833+
// FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers.
834+
let typeid = typeid_for_fnabi(bx.tcx(), fn_abi);
835+
let typeid_metadata = bx.typeid_metadata(typeid.clone());
836+
837+
// Test whether the function pointer is associated with the type identifier.
838+
let cond = bx.type_test(fn_ptr, typeid_metadata);
839+
let mut bx_pass = bx.build_sibling_block("type_test.pass");
840+
let mut bx_fail = bx.build_sibling_block("type_test.fail");
841+
bx.cond_br(cond, bx_pass.llbb(), bx_fail.llbb());
842+
843+
helper.do_call(
844+
self,
845+
&mut bx_pass,
846+
fn_abi,
847+
fn_ptr,
848+
&llargs,
849+
destination.as_ref().map(|&(_, target)| (ret_dest, target)),
850+
cleanup,
851+
);
852+
853+
bx_fail.abort();
854+
bx_fail.unreachable();
855+
856+
return;
857+
}
858+
827859
helper.do_call(
828860
self,
829861
&mut bx,

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_middle::mir;
44
use rustc_middle::mir::interpret::ErrorHandled;
55
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
66
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
7+
use rustc_symbol_mangling::typeid_for_fnabi;
78
use rustc_target::abi::call::{FnAbi, PassMode};
89

910
use std::iter;
@@ -244,6 +245,13 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
244245
for (bb, _) in traversal::reverse_postorder(&mir) {
245246
fx.codegen_block(bb);
246247
}
248+
249+
// For backends that support CFI using type membership (i.e., testing whether a given pointer
250+
// is associated with a type identifier).
251+
if cx.tcx().sess.is_sanitizer_cfi_enabled() {
252+
let typeid = typeid_for_fnabi(cx.tcx(), fn_abi);
253+
bx.type_metadata(llfn, typeid.clone());
254+
}
247255
}
248256

249257
/// Produces, for each argument, a `Value` pointing at the

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ pub trait BuilderMethods<'a, 'tcx>:
158158

159159
fn range_metadata(&mut self, load: Self::Value, range: WrappingRange);
160160
fn nonnull_metadata(&mut self, load: Self::Value);
161+
fn type_metadata(&mut self, function: Self::Function, typeid: String);
162+
fn typeid_metadata(&mut self, typeid: String) -> Self::Value;
161163

162164
fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value;
163165
fn store_with_flags(

compiler/rustc_codegen_ssa/src/traits/intrinsic.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
2424
///
2525
/// Currently has any effect only when LLVM versions prior to 12.0 are used as the backend.
2626
fn sideeffect(&mut self);
27+
/// Trait method used to test whether a given pointer is associated with a type identifier.
28+
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value;
2729
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
2830
/// Rust defined C-variadic functions.
2931
fn va_start(&mut self, val: Self::Value) -> Self::Value;

0 commit comments

Comments
 (0)