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
26 changed files with 231 additions and 28 deletions
12
Cargo.lock
12
Cargo.lock
|
@ -4269,6 +4269,7 @@ dependencies = [
|
||||||
"rustc_span",
|
"rustc_span",
|
||||||
"rustc_target",
|
"rustc_target",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"twox-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -5197,6 +5198,17 @@ dependencies = [
|
||||||
"tracing-subscriber",
|
"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]]
|
[[package]]
|
||||||
name = "type-map"
|
name = "type-map"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
@ -300,4 +300,8 @@ impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
// Unsupported.
|
// Unsupported.
|
||||||
self.context.new_rvalue_from_int(self.int_type, 0)
|
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,
|
callee,
|
||||||
args.as_ptr(),
|
args.as_ptr(),
|
||||||
args.len() as c_uint,
|
args.len() as c_uint,
|
||||||
None,
|
[].as_ptr(),
|
||||||
|
0 as c_uint,
|
||||||
);
|
);
|
||||||
llvm::LLVMSetTailCall(ret, True);
|
llvm::LLVMSetTailCall(ret, True);
|
||||||
if output.is_some() {
|
if output.is_some() {
|
||||||
|
@ -132,8 +133,15 @@ pub(crate) unsafe fn codegen(
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
|
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let ret =
|
let ret = llvm::LLVMRustBuildCall(
|
||||||
llvm::LLVMRustBuildCall(llbuilder, ty, callee, args.as_ptr(), args.len() as c_uint, None);
|
llbuilder,
|
||||||
|
ty,
|
||||||
|
callee,
|
||||||
|
args.as_ptr(),
|
||||||
|
args.len() as c_uint,
|
||||||
|
[].as_ptr(),
|
||||||
|
0 as c_uint,
|
||||||
|
);
|
||||||
llvm::LLVMSetTailCall(ret, True);
|
llvm::LLVMSetTailCall(ret, True);
|
||||||
llvm::LLVMBuildRetVoid(llbuilder);
|
llvm::LLVMBuildRetVoid(llbuilder);
|
||||||
llvm::LLVMDisposeBuilder(llbuilder);
|
llvm::LLVMDisposeBuilder(llbuilder);
|
||||||
|
|
|
@ -20,6 +20,7 @@ use rustc_middle::ty::layout::{
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_span::Span;
|
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::abi::{self, call::FnAbi, Align, Size, WrappingRange};
|
||||||
use rustc_target::spec::{HasTargetSpec, Target};
|
use rustc_target::spec::{HasTargetSpec, Target};
|
||||||
use std::borrow::Cow;
|
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);
|
debug!("invoke {:?} with args ({:?})", llfn, args);
|
||||||
|
|
||||||
let args = self.check_call("invoke", llty, llfn, args);
|
let args = self.check_call("invoke", llty, llfn, args);
|
||||||
let bundle = funclet.map(|funclet| funclet.bundle());
|
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
|
||||||
let bundle = bundle.as_ref().map(|b| &*b.raw);
|
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 {
|
let invoke = unsafe {
|
||||||
llvm::LLVMRustBuildInvoke(
|
llvm::LLVMRustBuildInvoke(
|
||||||
self.llbuilder,
|
self.llbuilder,
|
||||||
|
@ -237,7 +254,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
args.len() as c_uint,
|
args.len() as c_uint,
|
||||||
then,
|
then,
|
||||||
catch,
|
catch,
|
||||||
bundle,
|
bundles.as_ptr(),
|
||||||
|
bundles.len() as c_uint,
|
||||||
UNNAMED,
|
UNNAMED,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -1143,7 +1161,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
llfn,
|
llfn,
|
||||||
args.as_ptr() as *const &llvm::Value,
|
args.as_ptr() as *const &llvm::Value,
|
||||||
args.len() as c_uint,
|
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);
|
debug!("call {:?} with args ({:?})", llfn, args);
|
||||||
|
|
||||||
let args = self.check_call("call", llty, llfn, args);
|
let args = self.check_call("call", llty, llfn, args);
|
||||||
let bundle = funclet.map(|funclet| funclet.bundle());
|
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
|
||||||
let bundle = bundle.as_ref().map(|b| &*b.raw);
|
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 {
|
let call = unsafe {
|
||||||
llvm::LLVMRustBuildCall(
|
llvm::LLVMRustBuildCall(
|
||||||
self.llbuilder,
|
self.llbuilder,
|
||||||
|
@ -1169,7 +1204,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
llfn,
|
llfn,
|
||||||
args.as_ptr() as *const &llvm::Value,
|
args.as_ptr() as *const &llvm::Value,
|
||||||
args.len() as c_uint,
|
args.len() as c_uint,
|
||||||
bundle,
|
bundles.as_ptr(),
|
||||||
|
bundles.len() as c_uint,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if let Some(fn_abi) = fn_abi {
|
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.
|
// Control Flow Guard is currently only supported by the MSVC linker on Windows.
|
||||||
if sess.target.is_like_msvc {
|
if sess.target.is_like_msvc {
|
||||||
match sess.opts.cg.control_flow_guard {
|
match sess.opts.cg.control_flow_guard {
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::type_::Type;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use rustc_codegen_ssa::traits::TypeMembershipMethods;
|
use rustc_codegen_ssa::traits::TypeMembershipMethods;
|
||||||
use rustc_middle::ty::Ty;
|
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;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
/// Declare a function.
|
/// Declare a function.
|
||||||
|
@ -136,6 +136,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||||
self.set_type_metadata(llfn, typeid);
|
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
|
llfn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -427,6 +427,7 @@ pub enum MetadataType {
|
||||||
MD_type = 19,
|
MD_type = 19,
|
||||||
MD_vcall_visibility = 28,
|
MD_vcall_visibility = 28,
|
||||||
MD_noundef = 29,
|
MD_noundef = 29,
|
||||||
|
MD_kcfi_type = 36,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// LLVMRustAsmDialect
|
/// LLVMRustAsmDialect
|
||||||
|
@ -1060,6 +1061,7 @@ extern "C" {
|
||||||
pub fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
|
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 LLVMRustGlobalAddMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
|
||||||
pub fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
|
pub fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
|
||||||
|
pub fn LLVMIsAFunction(Val: &Value) -> Option<&Value>;
|
||||||
|
|
||||||
// Operations on constants of any type
|
// Operations on constants of any type
|
||||||
pub fn LLVMConstNull(Ty: &Type) -> &Value;
|
pub fn LLVMConstNull(Ty: &Type) -> &Value;
|
||||||
|
@ -1270,7 +1272,8 @@ extern "C" {
|
||||||
NumArgs: c_uint,
|
NumArgs: c_uint,
|
||||||
Then: &'a BasicBlock,
|
Then: &'a BasicBlock,
|
||||||
Catch: &'a BasicBlock,
|
Catch: &'a BasicBlock,
|
||||||
Bundle: Option<&OperandBundleDef<'a>>,
|
OpBundles: *const Option<&OperandBundleDef<'a>>,
|
||||||
|
NumOpBundles: c_uint,
|
||||||
Name: *const c_char,
|
Name: *const c_char,
|
||||||
) -> &'a Value;
|
) -> &'a Value;
|
||||||
pub fn LLVMBuildLandingPad<'a>(
|
pub fn LLVMBuildLandingPad<'a>(
|
||||||
|
@ -1640,7 +1643,8 @@ extern "C" {
|
||||||
Fn: &'a Value,
|
Fn: &'a Value,
|
||||||
Args: *const &'a Value,
|
Args: *const &'a Value,
|
||||||
NumArgs: c_uint,
|
NumArgs: c_uint,
|
||||||
Bundle: Option<&OperandBundleDef<'a>>,
|
OpBundles: *const Option<&OperandBundleDef<'a>>,
|
||||||
|
NumOpBundles: c_uint,
|
||||||
) -> &'a Value;
|
) -> &'a Value;
|
||||||
pub fn LLVMRustBuildMemCpy<'a>(
|
pub fn LLVMRustBuildMemCpy<'a>(
|
||||||
B: &Builder<'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> {
|
pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> {
|
||||||
fn set_type_metadata(&self, function: Self::Function, typeid: String);
|
fn set_type_metadata(&self, function: Self::Function, typeid: String);
|
||||||
fn typeid_metadata(&self, typeid: String) -> Self::Value;
|
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> {
|
pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
|
||||||
|
|
|
@ -394,7 +394,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
|
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
|
||||||
gated!(
|
gated!(
|
||||||
no_sanitize, Normal,
|
no_sanitize, Normal,
|
||||||
template!(List: "address, memory, thread"), DuplicatesOk,
|
template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
|
||||||
experimental!(no_sanitize)
|
experimental!(no_sanitize)
|
||||||
),
|
),
|
||||||
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
|
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;
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
|
||||||
} else if item.has_name(sym::cfi) {
|
} else if item.has_name(sym::cfi) {
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::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) {
|
} else if item.has_name(sym::memory) {
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
||||||
} else if item.has_name(sym::memtag) {
|
} else if item.has_name(sym::memtag) {
|
||||||
|
@ -1872,7 +1874,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
||||||
} else {
|
} else {
|
||||||
tcx.sess
|
tcx.sess
|
||||||
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
.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();
|
.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1460,13 +1460,13 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) {
|
||||||
|
|
||||||
extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||||
LLVMValueRef *Args, unsigned NumArgs,
|
LLVMValueRef *Args, unsigned NumArgs,
|
||||||
OperandBundleDef *Bundle) {
|
OperandBundleDef **OpBundles,
|
||||||
|
unsigned NumOpBundles) {
|
||||||
Value *Callee = unwrap(Fn);
|
Value *Callee = unwrap(Fn);
|
||||||
FunctionType *FTy = unwrap<FunctionType>(Ty);
|
FunctionType *FTy = unwrap<FunctionType>(Ty);
|
||||||
unsigned Len = Bundle ? 1 : 0;
|
|
||||||
ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
|
|
||||||
return wrap(unwrap(B)->CreateCall(
|
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) {
|
extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
|
||||||
|
@ -1506,14 +1506,14 @@ extern "C" LLVMValueRef
|
||||||
LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||||
LLVMValueRef *Args, unsigned NumArgs,
|
LLVMValueRef *Args, unsigned NumArgs,
|
||||||
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
|
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
|
||||||
OperandBundleDef *Bundle, const char *Name) {
|
OperandBundleDef **OpBundles, unsigned NumOpBundles,
|
||||||
|
const char *Name) {
|
||||||
Value *Callee = unwrap(Fn);
|
Value *Callee = unwrap(Fn);
|
||||||
FunctionType *FTy = unwrap<FunctionType>(Ty);
|
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),
|
return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch),
|
||||||
makeArrayRef(unwrap(Args), NumArgs),
|
makeArrayRef(unwrap(Args), NumArgs),
|
||||||
Bundles, Name));
|
makeArrayRef(*OpBundles, NumOpBundles),
|
||||||
|
Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
|
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_opt_panic_strategy: &str = parse_panic_strategy;
|
||||||
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
|
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_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_sanitizer_memory_track_origins: &str = "0, 1, or 2";
|
||||||
pub const parse_cfguard: &str =
|
pub const parse_cfguard: &str =
|
||||||
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
|
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
|
||||||
|
@ -675,6 +675,7 @@ mod parse {
|
||||||
*slot |= match s {
|
*slot |= match s {
|
||||||
"address" => SanitizerSet::ADDRESS,
|
"address" => SanitizerSet::ADDRESS,
|
||||||
"cfi" => SanitizerSet::CFI,
|
"cfi" => SanitizerSet::CFI,
|
||||||
|
"kcfi" => SanitizerSet::KCFI,
|
||||||
"leak" => SanitizerSet::LEAK,
|
"leak" => SanitizerSet::LEAK,
|
||||||
"memory" => SanitizerSet::MEMORY,
|
"memory" => SanitizerSet::MEMORY,
|
||||||
"memtag" => SanitizerSet::MEMTAG,
|
"memtag" => SanitizerSet::MEMTAG,
|
||||||
|
|
|
@ -683,6 +683,10 @@ impl Session {
|
||||||
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
|
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.
|
/// Check whether this compile session and crate type use static crt.
|
||||||
pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
|
pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
|
||||||
if !self.target.crt_static_respected {
|
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.opts.unstable_opts.stack_protector != StackProtector::None {
|
||||||
if !sess.target.options.supports_stack_protector {
|
if !sess.target.options.supports_stack_protector {
|
||||||
sess.emit_warning(StackProtectorNotSupportedForTarget {
|
sess.emit_warning(StackProtectorNotSupportedForTarget {
|
||||||
|
|
|
@ -828,6 +828,7 @@ symbols! {
|
||||||
item_like_imports,
|
item_like_imports,
|
||||||
iter,
|
iter,
|
||||||
iter_repeat,
|
iter_repeat,
|
||||||
|
kcfi,
|
||||||
keyword,
|
keyword,
|
||||||
kind,
|
kind,
|
||||||
kreg,
|
kreg,
|
||||||
|
|
|
@ -10,6 +10,7 @@ bitflags = "1.2.1"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
punycode = "0.4.0"
|
punycode = "0.4.0"
|
||||||
rustc-demangle = "0.1.21"
|
rustc-demangle = "0.1.21"
|
||||||
|
twox-hash = "1.6.3"
|
||||||
|
|
||||||
rustc_span = { path = "../rustc_span" }
|
rustc_span = { path = "../rustc_span" }
|
||||||
rustc_middle = { path = "../rustc_middle" }
|
rustc_middle = { path = "../rustc_middle" }
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
|
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
|
||||||
use rustc_target::abi::call::FnAbi;
|
use rustc_target::abi::call::FnAbi;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
use twox_hash::XxHash64;
|
||||||
|
|
||||||
mod typeid_itanium_cxx_abi;
|
mod typeid_itanium_cxx_abi;
|
||||||
use typeid_itanium_cxx_abi::TypeIdOptions;
|
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 {
|
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)
|
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`.
|
// 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 {
|
pub fn target() -> Target {
|
||||||
let opts = TargetOptions {
|
let opts = TargetOptions {
|
||||||
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
||||||
linker: Some("rust-lld".into()),
|
linker: Some("rust-lld".into()),
|
||||||
features: "+strict-align,+neon,+fp-armv8".into(),
|
features: "+strict-align,+neon,+fp-armv8".into(),
|
||||||
|
supported_sanitizers: SanitizerSet::KCFI,
|
||||||
relocation_model: RelocModel::Static,
|
relocation_model: RelocModel::Static,
|
||||||
disable_redzone: true,
|
disable_redzone: true,
|
||||||
max_atomic_width: Some(128),
|
max_atomic_width: Some(128),
|
||||||
|
|
|
@ -803,7 +803,7 @@ impl ToJson for StackProbeType {
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
#[derive(Default, Encodable, Decodable)]
|
#[derive(Default, Encodable, Decodable)]
|
||||||
pub struct SanitizerSet: u8 {
|
pub struct SanitizerSet: u16 {
|
||||||
const ADDRESS = 1 << 0;
|
const ADDRESS = 1 << 0;
|
||||||
const LEAK = 1 << 1;
|
const LEAK = 1 << 1;
|
||||||
const MEMORY = 1 << 2;
|
const MEMORY = 1 << 2;
|
||||||
|
@ -812,6 +812,7 @@ bitflags::bitflags! {
|
||||||
const CFI = 1 << 5;
|
const CFI = 1 << 5;
|
||||||
const MEMTAG = 1 << 6;
|
const MEMTAG = 1 << 6;
|
||||||
const SHADOWCALLSTACK = 1 << 7;
|
const SHADOWCALLSTACK = 1 << 7;
|
||||||
|
const KCFI = 1 << 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,6 +824,7 @@ impl SanitizerSet {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
SanitizerSet::ADDRESS => "address",
|
SanitizerSet::ADDRESS => "address",
|
||||||
SanitizerSet::CFI => "cfi",
|
SanitizerSet::CFI => "cfi",
|
||||||
|
SanitizerSet::KCFI => "kcfi",
|
||||||
SanitizerSet::LEAK => "leak",
|
SanitizerSet::LEAK => "leak",
|
||||||
SanitizerSet::MEMORY => "memory",
|
SanitizerSet::MEMORY => "memory",
|
||||||
SanitizerSet::MEMTAG => "memtag",
|
SanitizerSet::MEMTAG => "memtag",
|
||||||
|
@ -858,6 +860,7 @@ impl IntoIterator for SanitizerSet {
|
||||||
[
|
[
|
||||||
SanitizerSet::ADDRESS,
|
SanitizerSet::ADDRESS,
|
||||||
SanitizerSet::CFI,
|
SanitizerSet::CFI,
|
||||||
|
SanitizerSet::KCFI,
|
||||||
SanitizerSet::LEAK,
|
SanitizerSet::LEAK,
|
||||||
SanitizerSet::MEMORY,
|
SanitizerSet::MEMORY,
|
||||||
SanitizerSet::MEMTAG,
|
SanitizerSet::MEMTAG,
|
||||||
|
@ -2292,6 +2295,7 @@ impl Target {
|
||||||
base.$key_name |= match s.as_str() {
|
base.$key_name |= match s.as_str() {
|
||||||
Some("address") => SanitizerSet::ADDRESS,
|
Some("address") => SanitizerSet::ADDRESS,
|
||||||
Some("cfi") => SanitizerSet::CFI,
|
Some("cfi") => SanitizerSet::CFI,
|
||||||
|
Some("kcfi") => SanitizerSet::KCFI,
|
||||||
Some("leak") => SanitizerSet::LEAK,
|
Some("leak") => SanitizerSet::LEAK,
|
||||||
Some("memory") => SanitizerSet::MEMORY,
|
Some("memory") => SanitizerSet::MEMORY,
|
||||||
Some("memtag") => SanitizerSet::MEMTAG,
|
Some("memtag") => SanitizerSet::MEMTAG,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// features.
|
// features.
|
||||||
|
|
||||||
use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy};
|
use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy};
|
||||||
use super::{RelroLevel, StackProbeType, Target, TargetOptions};
|
use super::{RelroLevel, SanitizerSet, StackProbeType, Target, TargetOptions};
|
||||||
|
|
||||||
pub fn target() -> Target {
|
pub fn target() -> Target {
|
||||||
let opts = TargetOptions {
|
let opts = TargetOptions {
|
||||||
|
@ -20,6 +20,7 @@ pub fn target() -> Target {
|
||||||
features:
|
features:
|
||||||
"-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
|
"-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
|
||||||
.into(),
|
.into(),
|
||||||
|
supported_sanitizers: SanitizerSet::KCFI,
|
||||||
disable_redzone: true,
|
disable_redzone: true,
|
||||||
panic_strategy: PanicStrategy::Abort,
|
panic_strategy: PanicStrategy::Abort,
|
||||||
code_model: Some(CodeModel::Kernel),
|
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)]
|
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
|
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_asm_support = config.has_asm_support();
|
||||||
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||||
let has_cfi = util::CFI_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_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||||
let has_msan = util::MSAN_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);
|
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");
|
&& config.parse_name_directive(ln, "needs-sanitizer-support");
|
||||||
ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address");
|
ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address");
|
||||||
ignore |= !has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi");
|
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_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak");
|
||||||
ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
|
ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
|
||||||
ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");
|
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",
|
"x86_64-unknown-netbsd",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub const KCFI_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-none", "x86_64-linux-none"];
|
||||||
|
|
||||||
pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
|
pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
|
||||||
// FIXME: currently broken, see #88132
|
// FIXME: currently broken, see #88132
|
||||||
// "aarch64-apple-darwin",
|
// "aarch64-apple-darwin",
|
||||||
|
|
|
@ -214,6 +214,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
||||||
"snap",
|
"snap",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
"stacker",
|
"stacker",
|
||||||
|
"static_assertions",
|
||||||
"syn",
|
"syn",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
@ -234,6 +235,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-tree",
|
"tracing-tree",
|
||||||
|
"twox-hash",
|
||||||
"type-map",
|
"type-map",
|
||||||
"typenum",
|
"typenum",
|
||||||
"unic-char-property",
|
"unic-char-property",
|
||||||
|
|
Loading…
Add table
Reference in a new issue