mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Rollup merge of #105109 - rcvalle:rust-kcfi, r=bjorn3
Add LLVM KCFI support to the Rust compiler This PR 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. Thank you again, `@bjorn3,` `@eddyb,` `@nagisa,` and `@ojeda,` for all the help!
This commit is contained in:
commit
947fe7e341
12
Cargo.lock
12
Cargo.lock
@ -4403,6 +4403,7 @@ dependencies = [
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"tracing",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5393,6 +5394,17 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"rand 0.8.5",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "type-map"
|
||||
version = "0.4.0"
|
||||
|
@ -300,4 +300,8 @@ impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
// Unsupported.
|
||||
self.context.new_rvalue_from_int(self.int_type, 0)
|
||||
}
|
||||
|
||||
fn set_kcfi_type_metadata(&self, _function: RValue<'gcc>, _kcfi_typeid: u32) {
|
||||
// Unsupported.
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -427,6 +427,7 @@ pub enum MetadataType {
|
||||
MD_type = 19,
|
||||
MD_vcall_visibility = 28,
|
||||
MD_noundef = 29,
|
||||
MD_kcfi_type = 36,
|
||||
}
|
||||
|
||||
/// LLVMRustAsmDialect
|
||||
@ -1063,6 +1064,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;
|
||||
@ -1273,7 +1275,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>(
|
||||
@ -1643,7 +1646,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>,
|
||||
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
|
||||
pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> {
|
||||
fn set_type_metadata(&self, function: Self::Function, typeid: String);
|
||||
fn typeid_metadata(&self, typeid: String) -> Self::Value;
|
||||
fn set_kcfi_type_metadata(&self, function: Self::Function, typeid: u32);
|
||||
}
|
||||
|
||||
pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
|
||||
|
@ -394,7 +394,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
|
||||
gated!(
|
||||
no_sanitize, Normal,
|
||||
template!(List: "address, memory, thread"), DuplicatesOk,
|
||||
template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
|
||||
experimental!(no_sanitize)
|
||||
),
|
||||
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
|
||||
|
@ -1846,6 +1846,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
|
||||
} else if item.has_name(sym::cfi) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
|
||||
} else if item.has_name(sym::kcfi) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI;
|
||||
} else if item.has_name(sym::memory) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
||||
} else if item.has_name(sym::memtag) {
|
||||
@ -1859,7 +1861,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
||||
} else {
|
||||
tcx.sess
|
||||
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
||||
.note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
|
||||
.note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
@ -1476,13 +1476,13 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) {
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
OperandBundleDef *Bundle) {
|
||||
OperandBundleDef **OpBundles,
|
||||
unsigned NumOpBundles) {
|
||||
Value *Callee = unwrap(Fn);
|
||||
FunctionType *FTy = unwrap<FunctionType>(Ty);
|
||||
unsigned Len = Bundle ? 1 : 0;
|
||||
ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
|
||||
return wrap(unwrap(B)->CreateCall(
|
||||
FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles));
|
||||
FTy, Callee, makeArrayRef(unwrap(Args), NumArgs),
|
||||
makeArrayRef(*OpBundles, NumOpBundles)));
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
|
||||
@ -1522,14 +1522,14 @@ extern "C" LLVMValueRef
|
||||
LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
|
||||
OperandBundleDef *Bundle, const char *Name) {
|
||||
OperandBundleDef **OpBundles, unsigned NumOpBundles,
|
||||
const char *Name) {
|
||||
Value *Callee = unwrap(Fn);
|
||||
FunctionType *FTy = unwrap<FunctionType>(Ty);
|
||||
unsigned Len = Bundle ? 1 : 0;
|
||||
ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
|
||||
return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch),
|
||||
makeArrayRef(unwrap(Args), NumArgs),
|
||||
Bundles, Name));
|
||||
makeArrayRef(*OpBundles, NumOpBundles),
|
||||
Name));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
|
||||
|
@ -368,7 +368,7 @@ mod desc {
|
||||
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
|
||||
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
|
||||
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
|
||||
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
|
||||
pub const parse_cfguard: &str =
|
||||
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
|
||||
@ -675,6 +675,7 @@ mod parse {
|
||||
*slot |= match s {
|
||||
"address" => SanitizerSet::ADDRESS,
|
||||
"cfi" => SanitizerSet::CFI,
|
||||
"kcfi" => SanitizerSet::KCFI,
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"memtag" => SanitizerSet::MEMTAG,
|
||||
|
@ -686,6 +686,10 @@ impl Session {
|
||||
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
|
||||
}
|
||||
|
||||
pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
|
||||
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
|
||||
}
|
||||
|
||||
/// Check whether this compile session and crate type use static crt.
|
||||
pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
|
||||
if !self.target.crt_static_respected {
|
||||
@ -1544,6 +1548,14 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
||||
}
|
||||
}
|
||||
|
||||
// LLVM CFI and KCFI are mutually exclusive
|
||||
if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() {
|
||||
sess.emit_err(CannotMixAndMatchSanitizers {
|
||||
first: "cfi".to_string(),
|
||||
second: "kcfi".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if sess.opts.unstable_opts.stack_protector != StackProtector::None {
|
||||
if !sess.target.options.supports_stack_protector {
|
||||
sess.emit_warning(StackProtectorNotSupportedForTarget {
|
||||
|
@ -828,6 +828,7 @@ symbols! {
|
||||
item_like_imports,
|
||||
iter,
|
||||
iter_repeat,
|
||||
kcfi,
|
||||
keyword,
|
||||
kind,
|
||||
kreg,
|
||||
|
@ -10,6 +10,7 @@ bitflags = "1.2.1"
|
||||
tracing = "0.1"
|
||||
punycode = "0.4.0"
|
||||
rustc-demangle = "0.1.21"
|
||||
twox-hash = "1.6.3"
|
||||
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use std::hash::Hasher;
|
||||
use twox_hash::XxHash64;
|
||||
|
||||
mod typeid_itanium_cxx_abi;
|
||||
use typeid_itanium_cxx_abi::TypeIdOptions;
|
||||
@ -16,3 +18,25 @@ pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>)
|
||||
pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String {
|
||||
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS)
|
||||
}
|
||||
|
||||
/// Returns an LLVM KCFI type metadata identifier for the specified FnAbi.
|
||||
pub fn kcfi_typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> u32 {
|
||||
// An LLVM KCFI type metadata identifier is a 32-bit constant produced by taking the lower half
|
||||
// of the xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
|
||||
let mut hash: XxHash64 = Default::default();
|
||||
hash.write(
|
||||
typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS).as_bytes(),
|
||||
);
|
||||
hash.finish() as u32
|
||||
}
|
||||
|
||||
/// Returns an LLVM KCFI type metadata identifier for the specified FnSig.
|
||||
pub fn kcfi_typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> u32 {
|
||||
// An LLVM KCFI type metadata identifier is a 32-bit constant produced by taking the lower half
|
||||
// of the xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
|
||||
let mut hash: XxHash64 = Default::default();
|
||||
hash.write(
|
||||
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS).as_bytes(),
|
||||
);
|
||||
hash.finish() as u32
|
||||
}
|
||||
|
@ -6,13 +6,16 @@
|
||||
//
|
||||
// For example, `-C target-cpu=cortex-a53`.
|
||||
|
||||
use super::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions};
|
||||
use super::{
|
||||
Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, Target, TargetOptions,
|
||||
};
|
||||
|
||||
pub fn target() -> Target {
|
||||
let opts = TargetOptions {
|
||||
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
||||
linker: Some("rust-lld".into()),
|
||||
features: "+strict-align,+neon,+fp-armv8".into(),
|
||||
supported_sanitizers: SanitizerSet::KCFI,
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
|
@ -804,7 +804,7 @@ impl ToJson for StackProbeType {
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Default, Encodable, Decodable)]
|
||||
pub struct SanitizerSet: u8 {
|
||||
pub struct SanitizerSet: u16 {
|
||||
const ADDRESS = 1 << 0;
|
||||
const LEAK = 1 << 1;
|
||||
const MEMORY = 1 << 2;
|
||||
@ -813,6 +813,7 @@ bitflags::bitflags! {
|
||||
const CFI = 1 << 5;
|
||||
const MEMTAG = 1 << 6;
|
||||
const SHADOWCALLSTACK = 1 << 7;
|
||||
const KCFI = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
@ -824,6 +825,7 @@ impl SanitizerSet {
|
||||
Some(match self {
|
||||
SanitizerSet::ADDRESS => "address",
|
||||
SanitizerSet::CFI => "cfi",
|
||||
SanitizerSet::KCFI => "kcfi",
|
||||
SanitizerSet::LEAK => "leak",
|
||||
SanitizerSet::MEMORY => "memory",
|
||||
SanitizerSet::MEMTAG => "memtag",
|
||||
@ -859,6 +861,7 @@ impl IntoIterator for SanitizerSet {
|
||||
[
|
||||
SanitizerSet::ADDRESS,
|
||||
SanitizerSet::CFI,
|
||||
SanitizerSet::KCFI,
|
||||
SanitizerSet::LEAK,
|
||||
SanitizerSet::MEMORY,
|
||||
SanitizerSet::MEMTAG,
|
||||
@ -2327,6 +2330,7 @@ impl Target {
|
||||
base.$key_name |= match s.as_str() {
|
||||
Some("address") => SanitizerSet::ADDRESS,
|
||||
Some("cfi") => SanitizerSet::CFI,
|
||||
Some("kcfi") => SanitizerSet::KCFI,
|
||||
Some("leak") => SanitizerSet::LEAK,
|
||||
Some("memory") => SanitizerSet::MEMORY,
|
||||
Some("memtag") => SanitizerSet::MEMTAG,
|
||||
|
@ -5,7 +5,7 @@
|
||||
// features.
|
||||
|
||||
use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy};
|
||||
use super::{RelroLevel, StackProbeType, Target, TargetOptions};
|
||||
use super::{RelroLevel, SanitizerSet, StackProbeType, Target, TargetOptions};
|
||||
|
||||
pub fn target() -> Target {
|
||||
let opts = TargetOptions {
|
||||
@ -20,6 +20,7 @@ pub fn target() -> Target {
|
||||
features:
|
||||
"-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
|
||||
.into(),
|
||||
supported_sanitizers: SanitizerSet::KCFI,
|
||||
disable_redzone: true,
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
code_model: Some(CodeModel::Kernel),
|
||||
|
@ -14,6 +14,9 @@ This feature allows for use of one of following sanitizers:
|
||||
forward-edge control flow protection.
|
||||
* [HWAddressSanitizer](#hwaddresssanitizer) a memory error detector similar to
|
||||
AddressSanitizer, but based on partial hardware assistance.
|
||||
* [KernelControlFlowIntegrity](#kernelcontrolflowintegrity) LLVM Kernel Control
|
||||
Flow Integrity (KCFI) provides forward-edge control flow protection for
|
||||
operating systems kernels.
|
||||
* [LeakSanitizer](#leaksanitizer) a run-time memory leak detector.
|
||||
* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
|
||||
* [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on
|
||||
@ -502,6 +505,32 @@ Registers where the failure occurred (pc 0xaaaae0ae4a98):
|
||||
SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94)
|
||||
```
|
||||
|
||||
# KernelControlFlowIntegrity
|
||||
|
||||
The LLVM Kernel Control Flow Integrity (CFI) support to the Rust compiler
|
||||
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 commit cff5bef "KCFI
|
||||
sanitizer"](https://github.com/llvm/llvm-project/commit/cff5bef948c91e4919de8a5fb9765e0edc13f3de).)
|
||||
|
||||
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 by defining and using
|
||||
compatible type identifiers (see Type metadata in the design document in the
|
||||
tracking issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
|
||||
|
||||
LLVM KCFI can be enabled with `-Zsanitizer=kcfi`.
|
||||
|
||||
LLVM KCFI is supported on the following targets:
|
||||
|
||||
* `aarch64-linux-android`
|
||||
* `aarch64-unknown-linux-gnu`
|
||||
* `x86_64-linux-android`
|
||||
* `x86_64-unknown-linux-gnu`
|
||||
|
||||
See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more
|
||||
details.
|
||||
|
||||
# LeakSanitizer
|
||||
|
||||
LeakSanitizer is run-time memory leak detector.
|
||||
@ -693,6 +722,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
|
||||
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
|
||||
[clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html
|
||||
[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
|
||||
[clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi
|
||||
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
|
||||
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
|
||||
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
|
||||
|
11
src/test/codegen/sanitizer-kcfi-add-kcfi-flag.rs
Normal file
11
src/test/codegen/sanitizer-kcfi-add-kcfi-flag.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// Verifies that "kcfi" module flag is added.
|
||||
//
|
||||
// needs-sanitizer-kcfi
|
||||
// compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi
|
||||
|
||||
#![crate_type="lib"]
|
||||
|
||||
pub fn foo() {
|
||||
}
|
||||
|
||||
// CHECK: !{{[0-9]+}} = !{i32 4, !"kcfi", i32 1}
|
@ -0,0 +1,47 @@
|
||||
// Verifies that KCFI type metadata for functions are emitted.
|
||||
//
|
||||
// revisions: aarch64 x86_64
|
||||
// [aarch64] compile-flags: --target aarch64-unknown-none
|
||||
// [aarch64] needs-llvm-components: aarch64
|
||||
// [x86_64] compile-flags: --target x86_64-unknown-none
|
||||
// [x86_64] needs-llvm-components:
|
||||
// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(no_core, lang_items)]
|
||||
#![no_core]
|
||||
|
||||
#[lang="sized"]
|
||||
trait Sized { }
|
||||
#[lang="copy"]
|
||||
trait Copy { }
|
||||
|
||||
impl Copy for i32 {}
|
||||
|
||||
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
|
||||
// CHECK-LABEL: define{{.*}}foo
|
||||
// FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
|
||||
// CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE1:[0-9]+]]
|
||||
// CHECK: call i32 %f(i32 %arg){{.*}}[ "kcfi"(i32 -1666898348) ]
|
||||
f(arg)
|
||||
}
|
||||
|
||||
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
|
||||
// CHECK-LABEL: define{{.*}}bar
|
||||
// FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
|
||||
// CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE2:[0-9]+]]
|
||||
// CHECK: call i32 %f(i32 %arg1, i32 %arg2){{.*}}[ "kcfi"(i32 -1789026986) ]
|
||||
f(arg1, arg2)
|
||||
}
|
||||
|
||||
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
|
||||
// CHECK-LABEL: define{{.*}}baz
|
||||
// FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
|
||||
// CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE3:[0-9]+]]
|
||||
// CHECK: call i32 %f(i32 %arg1, i32 %arg2, i32 %arg3){{.*}}[ "kcfi"(i32 1248878270) ]
|
||||
f(arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// CHECK: ![[TYPE1]] = !{i32 653723426}
|
||||
// CHECK: ![[TYPE2]] = !{i32 412174924}
|
||||
// CHECK: ![[TYPE3]] = !{i32 -636668840}
|
@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize`
|
||||
LL | #[no_sanitize(brontosaurus)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
|
||||
= note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -906,6 +906,7 @@ pub fn make_test_description<R: Read>(
|
||||
let has_asm_support = config.has_asm_support();
|
||||
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_kcfi = util::KCFI_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
@ -957,6 +958,7 @@ pub fn make_test_description<R: Read>(
|
||||
&& config.parse_name_directive(ln, "needs-sanitizer-support");
|
||||
ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address");
|
||||
ignore |= !has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi");
|
||||
ignore |= !has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi");
|
||||
ignore |= !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak");
|
||||
ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
|
||||
ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");
|
||||
|
@ -42,6 +42,8 @@ pub const CFI_SUPPORTED_TARGETS: &[&str] = &[
|
||||
"x86_64-unknown-netbsd",
|
||||
];
|
||||
|
||||
pub const KCFI_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-none", "x86_64-linux-none"];
|
||||
|
||||
pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
|
||||
// FIXME: currently broken, see #88132
|
||||
// "aarch64-apple-darwin",
|
||||
|
@ -219,6 +219,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
||||
"snap",
|
||||
"stable_deref_trait",
|
||||
"stacker",
|
||||
"static_assertions",
|
||||
"syn",
|
||||
"synstructure",
|
||||
"tempfile",
|
||||
@ -239,6 +240,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
"twox-hash",
|
||||
"type-map",
|
||||
"typenum",
|
||||
"unic-char-property",
|
||||
|
Loading…
Reference in New Issue
Block a user