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:
parent
b7bc90fea3
commit
65698ae9f3
12
Cargo.lock
12
Cargo.lock
@ -4269,6 +4269,7 @@ dependencies = [
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"tracing",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5197,6 +5198,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 @@ fn typeid_metadata(&self, _typeid: String) -> RValue<'gcc> {
|
||||
// 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::{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 @@ fn invoke(
|
||||
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 @@ fn invoke(
|
||||
args.len() as c_uint,
|
||||
then,
|
||||
catch,
|
||||
bundle,
|
||||
bundles.as_ptr(),
|
||||
bundles.len() as c_uint,
|
||||
UNNAMED,
|
||||
)
|
||||
};
|
||||
@ -1143,7 +1161,8 @@ fn instrprof_increment(
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
None,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1159,9 +1178,25 @@ fn call(
|
||||
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 @@ fn call(
|
||||
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::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 @@ pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Val
|
||||
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
|
||||
@ -1060,6 +1061,7 @@ pub fn LLVMStructTypeInContext<'a>(
|
||||
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 @@ pub fn LLVMRustBuildInvoke<'a>(
|
||||
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 @@ pub fn LLVMRustBuildCall<'a>(
|
||||
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 @@ fn typeid_metadata(&self, typeid: String) -> &'ll Value {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ fn scalar_pair_element_backend_type(
|
||||
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 struct 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)),
|
||||
|
@ -1859,6 +1859,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) {
|
||||
@ -1872,7 +1874,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();
|
||||
}
|
||||
}
|
||||
|
@ -1460,13 +1460,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) {
|
||||
@ -1506,14 +1506,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 @@ pub(crate) fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool
|
||||
*slot |= match s {
|
||||
"address" => SanitizerSet::ADDRESS,
|
||||
"cfi" => SanitizerSet::CFI,
|
||||
"kcfi" => SanitizerSet::KCFI,
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"memtag" => SanitizerSet::MEMTAG,
|
||||
|
@ -683,6 +683,10 @@ pub fn is_sanitizer_cfi_enabled(&self) -> bool {
|
||||
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 {
|
||||
@ -1530,6 +1534,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 @@
|
||||
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),
|
||||
|
@ -803,7 +803,7 @@ fn to_json(&self) -> Json {
|
||||
|
||||
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;
|
||||
@ -812,6 +812,7 @@ pub struct SanitizerSet: u8 {
|
||||
const CFI = 1 << 5;
|
||||
const MEMTAG = 1 << 6;
|
||||
const SHADOWCALLSTACK = 1 << 7;
|
||||
const KCFI = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
@ -823,6 +824,7 @@ pub fn as_str(self) -> Option<&'static str> {
|
||||
Some(match self {
|
||||
SanitizerSet::ADDRESS => "address",
|
||||
SanitizerSet::CFI => "cfi",
|
||||
SanitizerSet::KCFI => "kcfi",
|
||||
SanitizerSet::LEAK => "leak",
|
||||
SanitizerSet::MEMORY => "memory",
|
||||
SanitizerSet::MEMTAG => "memtag",
|
||||
@ -858,6 +860,7 @@ fn into_iter(self) -> Self::IntoIter {
|
||||
[
|
||||
SanitizerSet::ADDRESS,
|
||||
SanitizerSet::CFI,
|
||||
SanitizerSet::KCFI,
|
||||
SanitizerSet::LEAK,
|
||||
SanitizerSet::MEMORY,
|
||||
SanitizerSet::MEMTAG,
|
||||
@ -2292,6 +2295,7 @@ macro_rules! key {
|
||||
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),
|
||||
|
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 @@
|
||||
"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",
|
||||
|
@ -214,6 +214,7 @@
|
||||
"snap",
|
||||
"stable_deref_trait",
|
||||
"stacker",
|
||||
"static_assertions",
|
||||
"syn",
|
||||
"synstructure",
|
||||
"tempfile",
|
||||
@ -234,6 +235,7 @@
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
"twox-hash",
|
||||
"type-map",
|
||||
"typenum",
|
||||
"unic-char-property",
|
||||
|
Loading…
Reference in New Issue
Block a user