From 5d30e9318900dd4f034c21f1378fea7b40998b07 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Thu, 7 Oct 2021 15:33:13 -0700 Subject: 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). --- compiler/rustc_codegen_llvm/src/builder.rs | 26 ++++++++++++++++++++++++++ compiler/rustc_codegen_llvm/src/context.rs | 11 +++++++++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 8 ++++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 +++ 4 files changed, 48 insertions(+) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 9f7b8616d78..a94b7985987 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -604,6 +604,32 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + fn type_metadata(&mut self, function: &'ll Value, typeid: String) { + let typeid_metadata = self.typeid_metadata(typeid); + let v = [self.const_usize(0), typeid_metadata]; + unsafe { + llvm::LLVMGlobalSetMetadata( + function, + llvm::MD_type as c_uint, + llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( + self.cx.llcx, + v.as_ptr(), + v.len() as c_uint, + )), + ) + } + } + + fn typeid_metadata(&mut self, typeid: String) -> Self::Value { + unsafe { + llvm::LLVMMDStringInContext( + self.cx.llcx, + typeid.as_ptr() as *const c_char, + typeid.as_bytes().len() as c_uint, + ) + } + } + fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value { self.store_with_flags(val, ptr, align, MemFlags::empty()) } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 257a0ac89d8..cda766039c1 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -221,6 +221,15 @@ pub unsafe fn create_module( llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1); } + if sess.is_sanitizer_cfi_enabled() { + // FIXME(rcvalle): Add support for non canonical jump tables. + let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast(); + // FIXME(rcvalle): Add it with Override behavior flag--LLVMRustAddModuleFlag adds it with + // Warning behavior flag. Add support for specifying the behavior flag to + // LLVMRustAddModuleFlag. + llvm::LLVMRustAddModuleFlag(llmod, canonical_jump_tables, 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 { @@ -779,6 +788,8 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); } + ifn!("llvm.type.test", fn(i8p, self.type_metadata()) -> i1); + if self.sess().opts.debuginfo != DebugInfo::None { ifn!("llvm.dbg.declare", fn(self.type_metadata(), self.type_metadata()) -> void); ifn!("llvm.dbg.value", fn(self.type_metadata(), t_i64, self.type_metadata()) -> void); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c43141c7695..e63fb22829a 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -401,6 +401,14 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } + fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value { + // Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time + // optimization pass replaces calls to this intrinsic with code to test type membership. + let i8p_ty = self.type_i8p(); + let bitcast = self.bitcast(pointer, i8p_ty); + self.call_intrinsic("llvm.type.test", &[bitcast, typeid]) + } + fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value { self.call_intrinsic("llvm.va_start", &[va_list]) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 63eca00de2a..b258eb36aa8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -389,6 +389,7 @@ pub enum MetadataType { MD_nontemporal = 9, MD_mem_parallel_loop_access = 10, MD_nonnull = 11, + MD_type = 19, } /// LLVMRustAsmDialect @@ -975,6 +976,8 @@ extern "C" { pub fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); pub fn LLVMReplaceAllUsesWith(OldVal: &'a Value, NewVal: &'a Value); pub fn LLVMSetMetadata(Val: &'a Value, KindID: c_uint, Node: &'a Value); + pub fn LLVMGlobalSetMetadata(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); + pub fn LLVMValueAsMetadata(Node: &'a Value) -> &Metadata; // Operations on constants of any type pub fn LLVMConstNull(Ty: &Type) -> &Value; -- cgit 1.4.1-3-g733a5