Rollup merge of #105452 - rcvalle:rust-cfi-3, r=bjorn3

Add cross-language LLVM CFI support to the Rust compiler

This PR adds cross-language LLVM Control Flow Integrity (CFI) support to the Rust compiler by adding the `-Zsanitizer-cfi-normalize-integers` option to be used with Clang `-fsanitize-cfi-icall-normalize-integers` for normalizing integer types (see https://reviews.llvm.org/D139395).

It provides 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). For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, see design document in the tracking issue #89653.

Cross-language LLVM CFI can be enabled with -Zsanitizer=cfi and -Zsanitizer-cfi-normalize-integers, and requires proper (i.e., non-rustc) LTO (i.e., -Clinker-plugin-lto).

Thank you again, ``@bjorn3,`` ``@nikic,`` ``@samitolvanen,`` and the Rust community for all the help!
This commit is contained in:
Manish Goregaokar 2023-05-03 16:42:48 -07:00 committed by GitHub
commit 38bbc39895
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 1509 additions and 403 deletions

View File

@ -501,7 +501,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
if options.contains(InlineAsmOptions::NORETURN) {
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
self.call(self.type_void(), None, builtin_unreachable, &[], None);
self.call(self.type_void(), None, None, builtin_unreachable, &[], None);
}
// Write results to outputs.

View File

@ -35,6 +35,7 @@ use rustc_codegen_ssa::traits::{
};
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_span::Span;
@ -455,12 +456,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}
#[cfg(feature="master")]
fn invoke(&mut self, typ: Type<'gcc>, _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
fn invoke(&mut self, typ: Type<'gcc>, fn_attrs: Option<&CodegenFnAttrs>, _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
let try_block = self.current_func().new_block("try");
let current_block = self.block.clone();
self.block = try_block;
let call = self.call(typ, None, func, args, None); // TODO(antoyo): use funclet here?
let call = self.call(typ, fn_attrs, None, func, args, None); // TODO(antoyo): use funclet here?
self.block = current_block;
let return_value = self.current_func()
@ -483,8 +484,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}
#[cfg(not(feature="master"))]
fn invoke(&mut self, typ: Type<'gcc>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
let call_site = self.call(typ, None, func, args, None);
fn invoke(&mut self, typ: Type<'gcc>, fn_attrs: &CodegenFnAttrs, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
let call_site = self.call(typ, fn_attrs, None, func, args, None);
let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
self.llbb().end_with_conditional(None, condition, then, catch);
if let Some(_fn_abi) = fn_abi {
@ -1351,6 +1352,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
fn call(
&mut self,
_typ: Type<'gcc>,
_fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
func: RValue<'gcc>,
args: &[RValue<'gcc>],

View File

@ -113,7 +113,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
_ if simple.is_some() => {
// FIXME(antoyo): remove this cast when the API supports function.
let func = unsafe { std::mem::transmute(simple.expect("simple")) };
self.call(self.type_void(), None, func, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None)
self.call(self.type_void(), None, None, func, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None)
},
sym::likely => {
self.expect(args[0].immediate(), true)
@ -326,7 +326,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
let masked = self.and(addr, mask);
self.bitcast(masked, void_ptr_type)
},
_ if name_str.starts_with("simd_") => {
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
Ok(llval) => llval,
@ -354,7 +354,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
fn abort(&mut self) {
let func = self.context.get_builtin_function("abort");
let func: RValue<'gcc> = unsafe { std::mem::transmute(func) };
self.call(self.type_void(), None, func, &[], None);
self.call(self.type_void(), None, None, func, &[], None);
}
fn assume(&mut self, value: Self::Value) {
@ -1135,7 +1135,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
fn try_intrinsic<'a, 'b, 'gcc, 'tcx>(bx: &'b mut Builder<'a, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
if bx.sess().panic_strategy() == PanicStrategy::Abort {
bx.call(bx.type_void(), None, try_func, &[data], None);
bx.call(bx.type_void(), None, None, try_func, &[data], None);
// Return 0 unconditionally from the intrinsic call;
// we can never unwind.
let ret_align = bx.tcx.data_layout.i32_align.abi;
@ -1204,21 +1204,21 @@ fn codegen_gnu_try<'gcc>(bx: &mut Builder<'_, 'gcc, '_>, try_func: RValue<'gcc>,
let zero = bx.cx.context.new_rvalue_zero(bx.int_type);
let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]);
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
bx.call(catch_ty, None, catch_func, &[data, ptr], None);
bx.call(catch_ty, None, None, catch_func, &[data, ptr], None);
bx.ret(bx.const_i32(1));
// NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not
// generate a try/catch.
// FIXME(antoyo): add a check in the libgccjit API to prevent this.
bx.switch_to_block(current_block);
bx.invoke(try_func_ty, None, try_func, &[data], then, catch, None);
bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None);
});
let func = unsafe { std::mem::transmute(func) };
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = bx.call(llty, None, func, &[try_func, data, catch_func], None);
let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None);
let i32_align = bx.tcx().data_layout.i32_align.abi;
bx.store(ret, dest, i32_align);
}

View File

@ -280,16 +280,4 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
}
impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
fn set_type_metadata(&self, _function: RValue<'gcc>, _typeid: String) {
// Unsupported.
}
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.
}
}

View File

@ -443,9 +443,9 @@ pub(crate) fn inline_asm_call<'ll>(
);
let call = if let Some((dest, catch, funclet)) = dest_catch_funclet {
bx.invoke(fty, None, v, inputs, dest, catch, funclet)
bx.invoke(fty, None, None, v, inputs, dest, catch, funclet)
} else {
bx.call(fty, None, v, inputs, None)
bx.call(fty, None, None, v, inputs, None)
};
// Store mark in a metadata node so we can map LLVM errors

View File

@ -15,14 +15,15 @@ use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::MemFlags;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::ty::layout::{
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
use rustc_symbol_mangling::typeid::kcfi_typeid_for_fnabi;
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions};
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
use rustc_target::spec::{HasTargetSpec, Target};
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
use std::borrow::Cow;
use std::ffi::CStr;
use std::iter;
@ -216,6 +217,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
fn invoke(
&mut self,
llty: &'ll Type,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
args: &[&'ll Value],
@ -230,19 +232,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
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() && let Some(fn_abi) = fn_abi && is_indirect_call {
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
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);
}
// Emit CFI pointer type membership test
self.cfi_type_test(fn_attrs, fn_abi, llfn);
// Emit KCFI operand bundle
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
bundles.push(kcfi_bundle);
bundles.retain(|bundle| bundle.is_some());
let invoke = unsafe {
@ -1183,6 +1179,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
fn call(
&mut self,
llty: &'ll Type,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
args: &[&'ll Value],
@ -1195,19 +1192,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
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 let Some(fn_abi) = fn_abi && self.tcx.sess.is_sanitizer_kcfi_enabled() && is_indirect_call {
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
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);
}
// Emit CFI pointer type membership test
self.cfi_type_test(fn_attrs, fn_abi, llfn);
// Emit KCFI operand bundle
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
bundles.push(kcfi_bundle);
bundles.retain(|bundle| bundle.is_some());
let call = unsafe {
@ -1456,7 +1447,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value {
let (ty, f) = self.cx.get_intrinsic(intrinsic);
self.call(ty, None, f, args, None)
self.call(ty, None, None, f, args, None)
}
fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) {
@ -1518,7 +1509,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width)
};
let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
self.call(self.type_func(&[src_ty], dest_ty), None, f, &[val], None)
self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None)
}
pub(crate) fn landing_pad(
@ -1535,4 +1526,71 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
llvm::LLVMBuildLandingPad(self.llbuilder, ty, None, num_clauses as c_uint, UNNAMED)
}
}
// Emits CFI pointer type membership tests.
fn cfi_type_test(
&mut self,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
) {
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
if is_indirect_call && fn_abi.is_some() && self.tcx.sess.is_sanitizer_cfi_enabled() {
if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::CFI) {
return;
}
let mut options = TypeIdOptions::empty();
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
options.insert(TypeIdOptions::GENERALIZE_POINTERS);
}
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
}
let typeid = typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
// Test whether the function pointer is associated with the type identifier.
let cond = self.type_test(llfn, typeid_metadata);
let bb_pass = self.append_sibling_block("type_test.pass");
let bb_fail = self.append_sibling_block("type_test.fail");
self.cond_br(cond, bb_pass, bb_fail);
self.switch_to_block(bb_fail);
self.abort();
self.unreachable();
self.switch_to_block(bb_pass);
}
}
// Emits KCFI operand bundles.
fn kcfi_operand_bundle(
&mut self,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
) -> Option<llvm::OperandBundleDef<'ll>> {
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
let kcfi_bundle = if is_indirect_call && self.tcx.sess.is_sanitizer_kcfi_enabled() {
if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::KCFI) {
return None;
}
let mut options = TypeIdOptions::empty();
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
options.insert(TypeIdOptions::GENERALIZE_POINTERS);
}
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
}
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
} else {
None
};
kcfi_bundle
}
}

View File

@ -228,18 +228,29 @@ pub unsafe fn create_module<'ll>(
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
}
if sess.is_sanitizer_cfi_enabled() {
// FIXME(rcvalle): Add support for non canonical jump tables.
// Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.)
if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() {
let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast();
// FIXME(rcvalle): Add it with Override behavior flag.
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Warning,
llvm::LLVMModFlagBehavior::Override,
canonical_jump_tables,
1,
);
}
// Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.)
if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() {
let enable_split_lto_unit = "EnableSplitLTOUnit\0".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Override,
enable_split_lto_unit,
1,
);
}
// Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
if sess.is_sanitizer_kcfi_enabled() {
let kcfi = "kcfi\0".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);

View File

@ -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::{kcfi_typeid_for_fnabi, typeid_for_fnabi};
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions};
use smallvec::SmallVec;
/// Declare a function.
@ -132,12 +132,31 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
fn_abi.apply_attrs_llfn(self, llfn);
if self.tcx.sess.is_sanitizer_cfi_enabled() {
let typeid = typeid_for_fnabi(self.tcx, fn_abi);
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty());
self.set_type_metadata(llfn, typeid);
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS);
self.add_type_metadata(llfn, typeid);
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS);
self.add_type_metadata(llfn, typeid);
let typeid = typeid_for_fnabi(
self.tcx,
fn_abi,
TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS,
);
self.add_type_metadata(llfn, typeid);
}
if self.tcx.sess.is_sanitizer_kcfi_enabled() {
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
// LLVM KCFI does not support multiple !kcfi_type attachments
let mut options = TypeIdOptions::empty();
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
options.insert(TypeIdOptions::GENERALIZE_POINTERS);
}
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
}
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options);
self.set_kcfi_type_metadata(llfn, kcfi_typeid);
}

View File

@ -110,6 +110,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
self.call(
simple_ty,
None,
None,
simple_fn,
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
None,
@ -444,7 +445,7 @@ fn try_intrinsic<'ll>(
) {
if bx.sess().panic_strategy() == PanicStrategy::Abort {
let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
bx.call(try_func_ty, None, try_func, &[data], None);
bx.call(try_func_ty, None, None, try_func, &[data], None);
// Return 0 unconditionally from the intrinsic call;
// we can never unwind.
let ret_align = bx.tcx().data_layout.i32_align.abi;
@ -543,7 +544,7 @@ fn codegen_msvc_try<'ll>(
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
let slot = bx.alloca(bx.type_i8p(), ptr_align);
let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
bx.invoke(try_func_ty, None, try_func, &[data], normal, catchswitch, None);
bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None);
bx.switch_to_block(normal);
bx.ret(bx.const_i32(0));
@ -587,7 +588,7 @@ fn codegen_msvc_try<'ll>(
let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]);
let ptr = bx.load(bx.type_i8p(), slot, ptr_align);
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
bx.call(catch_ty, None, catch_func, &[data, ptr], Some(&funclet));
bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet));
bx.catch_ret(&funclet, caught);
// The flag value of 64 indicates a "catch-all".
@ -595,7 +596,7 @@ fn codegen_msvc_try<'ll>(
let flags = bx.const_i32(64);
let null = bx.const_null(bx.type_i8p());
let funclet = bx.catch_pad(cs, &[null, flags, null]);
bx.call(catch_ty, None, catch_func, &[data, null], Some(&funclet));
bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet));
bx.catch_ret(&funclet, caught);
bx.switch_to_block(caught);
@ -604,7 +605,7 @@ fn codegen_msvc_try<'ll>(
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = bx.call(llty, None, llfn, &[try_func, data, catch_func], None);
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None);
let i32_align = bx.tcx().data_layout.i32_align.abi;
bx.store(ret, dest, i32_align);
}
@ -647,7 +648,7 @@ fn codegen_gnu_try<'ll>(
let data = llvm::get_param(bx.llfn(), 1);
let catch_func = llvm::get_param(bx.llfn(), 2);
let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
bx.invoke(try_func_ty, None, try_func, &[data], then, catch, None);
bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None);
bx.switch_to_block(then);
bx.ret(bx.const_i32(0));
@ -665,13 +666,13 @@ fn codegen_gnu_try<'ll>(
bx.add_clause(vals, tydesc);
let ptr = bx.extract_value(vals, 0);
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
bx.call(catch_ty, None, catch_func, &[data, ptr], None);
bx.call(catch_ty, None, None, catch_func, &[data, ptr], None);
bx.ret(bx.const_i32(1));
});
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = bx.call(llty, None, llfn, &[try_func, data, catch_func], None);
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None);
let i32_align = bx.tcx().data_layout.i32_align.abi;
bx.store(ret, dest, i32_align);
}
@ -711,7 +712,7 @@ fn codegen_emcc_try<'ll>(
let data = llvm::get_param(bx.llfn(), 1);
let catch_func = llvm::get_param(bx.llfn(), 2);
let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
bx.invoke(try_func_ty, None, try_func, &[data], then, catch, None);
bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None);
bx.switch_to_block(then);
bx.ret(bx.const_i32(0));
@ -750,13 +751,13 @@ fn codegen_emcc_try<'ll>(
let catch_data = bx.bitcast(catch_data, bx.type_i8p());
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
bx.call(catch_ty, None, catch_func, &[data, catch_data], None);
bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None);
bx.ret(bx.const_i32(1));
});
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = bx.call(llty, None, llfn, &[try_func, data, catch_func], None);
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None);
let i32_align = bx.tcx().data_layout.i32_align.abi;
bx.store(ret, dest, i32_align);
}
@ -1205,6 +1206,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let c = bx.call(
fn_ty,
None,
None,
f,
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
None,
@ -1423,6 +1425,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let v = bx.call(
fn_ty,
None,
None,
f,
&[args[1].immediate(), alignment, mask, args[0].immediate()],
None,
@ -1564,6 +1567,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let v = bx.call(
fn_ty,
None,
None,
f,
&[args[0].immediate(), args[1].immediate(), alignment, mask],
None,
@ -2037,7 +2041,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty);
let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
let v = bx.call(fn_ty, None, f, &[lhs, rhs], None);
let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None);
return Ok(v);
}

View File

@ -291,8 +291,24 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn add_type_metadata(&self, function: &'ll Value, typeid: String) {
let typeid_metadata = self.typeid_metadata(typeid).unwrap();
let v = [self.const_usize(0), typeid_metadata];
unsafe {
llvm::LLVMRustGlobalAddMetadata(
function,
llvm::MD_type as c_uint,
llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
self.llcx,
v.as_ptr(),
v.len() as c_uint,
)),
)
}
}
fn set_type_metadata(&self, function: &'ll Value, typeid: String) {
let typeid_metadata = self.typeid_metadata(typeid);
let typeid_metadata = self.typeid_metadata(typeid).unwrap();
let v = [self.const_usize(0), typeid_metadata];
unsafe {
llvm::LLVMGlobalSetMetadata(
@ -307,13 +323,28 @@ impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
}
fn typeid_metadata(&self, typeid: String) -> &'ll Value {
unsafe {
fn typeid_metadata(&self, typeid: String) -> Option<&'ll Value> {
Some(unsafe {
llvm::LLVMMDStringInContext(
self.llcx,
typeid.as_ptr() as *const c_char,
typeid.len() as c_uint,
)
})
}
fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
let kcfi_type_metadata = self.const_u32(kcfi_typeid);
unsafe {
llvm::LLVMRustGlobalAddMetadata(
function,
llvm::MD_kcfi_type as c_uint,
llvm::LLVMMDNodeInContext2(
self.llcx,
&llvm::LLVMValueAsMetadata(kcfi_type_metadata),
1,
),
)
}
}

View File

@ -494,7 +494,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
(rust_main, start_ty, vec![arg_argc, arg_argv])
};
let result = bx.call(start_ty, None, start_fn, &args, None);
let result = bx.call(start_ty, None, None, start_fn, &args, None);
let cast = bx.intcast(result, cx.type_int(), true);
bx.ret(cast);

View File

@ -28,8 +28,9 @@ impl<'a, 'tcx> VirtualIndex {
if bx.cx().sess().opts.unstable_opts.virtual_function_elimination
&& bx.cx().sess().lto() == Lto::Fat
{
let typeid =
bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty)));
let typeid = bx
.typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty)))
.unwrap();
let vtable_byte_offset = self.0 * bx.data_layout().pointer_size.bytes();
let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
bx.pointercast(func, llty)

View File

@ -19,7 +19,6 @@ use rustc_middle::ty::{self, Instance, Ty};
use rustc_session::config::OptLevel;
use rustc_span::source_map::Span;
use rustc_span::{sym, Symbol};
use rustc_symbol_mangling::typeid::typeid_for_fnabi;
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
use rustc_target::spec::abi::Abi;
@ -163,6 +162,12 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
// do an invoke, otherwise do a call.
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
let fn_attrs = if bx.tcx().def_kind(fx.instance.def_id()).has_codegen_attrs() {
Some(bx.tcx().codegen_fn_attrs(fx.instance.def_id()))
} else {
None
};
if !fn_abi.can_unwind {
unwind = mir::UnwindAction::Unreachable;
}
@ -190,6 +195,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
};
let invokeret = bx.invoke(
fn_ty,
fn_attrs,
Some(&fn_abi),
fn_ptr,
&llargs,
@ -211,7 +217,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
}
MergingSucc::False
} else {
let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &llargs, self.funclet(fx));
let llret = bx.call(fn_ty, fn_attrs, Some(&fn_abi), fn_ptr, &llargs, self.funclet(fx));
if fx.mir[self.bb].is_cleanup {
// Cleanup is always the cold path. Don't inline
// drop glue. Also, when there is a deeply-nested
@ -1051,48 +1057,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.codegen_argument(bx, location, &mut llargs, last_arg);
}
let (is_indirect_call, fn_ptr) = match (llfn, instance) {
(Some(llfn), _) => (true, llfn),
(None, Some(instance)) => (false, bx.get_fn_addr(instance)),
_ => span_bug!(span, "no llfn for call"),
let fn_ptr = match (instance, llfn) {
(Some(instance), None) => bx.get_fn_addr(instance),
(_, Some(llfn)) => llfn,
_ => span_bug!(span, "no instance or llfn for call"),
};
// For backends that support CFI using type membership (i.e., testing whether a given
// pointer is associated with a type identifier).
if bx.tcx().sess.is_sanitizer_cfi_enabled() && is_indirect_call {
// Emit type metadata and checks.
// FIXME(rcvalle): Add support for generalized identifiers.
// FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers.
let typeid = typeid_for_fnabi(bx.tcx(), fn_abi);
let typeid_metadata = self.cx.typeid_metadata(typeid);
// Test whether the function pointer is associated with the type identifier.
let cond = bx.type_test(fn_ptr, typeid_metadata);
let bb_pass = bx.append_sibling_block("type_test.pass");
let bb_fail = bx.append_sibling_block("type_test.fail");
bx.cond_br(cond, bb_pass, bb_fail);
bx.switch_to_block(bb_pass);
let merging_succ = helper.do_call(
self,
bx,
fn_abi,
fn_ptr,
&llargs,
target.as_ref().map(|&target| (ret_dest, target)),
unwind,
&copied_constant_arguments,
false,
);
assert_eq!(merging_succ, MergingSucc::False);
bx.switch_to_block(bb_fail);
bx.abort();
bx.unreachable();
return MergingSucc::False;
}
helper.do_call(
self,
bx,
@ -1640,7 +1610,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicCannotUnwind);
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
let llret = bx.call(fn_ty, None, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
bx.do_not_inline(llret);
bx.unreachable();

View File

@ -697,7 +697,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let fn_ptr = bx.get_fn_addr(instance);
let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
bx.call(fn_ty, Some(fn_abi), fn_ptr, &[], None)
let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() {
Some(bx.tcx().codegen_fn_attrs(instance.def_id()))
} else {
None
};
bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None)
} else {
bx.get_static(def_id)
};

View File

@ -14,6 +14,7 @@ use crate::mir::operand::OperandRef;
use crate::mir::place::PlaceRef;
use crate::MemFlags;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout};
use rustc_middle::ty::Ty;
use rustc_span::Span;
@ -72,6 +73,7 @@ pub trait BuilderMethods<'a, 'tcx>:
fn invoke(
&mut self,
llty: Self::Type,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: Self::Value,
args: &[Self::Value],
@ -321,6 +323,7 @@ pub trait BuilderMethods<'a, 'tcx>:
fn call(
&mut self,
llty: Self::Type,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: Self::Value,
args: &[Self::Value],

View File

@ -128,12 +128,16 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
) -> Self::Type;
}
// For backends that support CFI using type membership (i.e., testing whether a given pointer is
// For backends that support CFI using type membership (i.e., testing whether a given pointer is
// associated with a type identifier).
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);
fn add_type_metadata(&self, _function: Self::Function, _typeid: String) {}
fn set_type_metadata(&self, _function: Self::Function, _typeid: String) {}
fn typeid_metadata(&self, _typeid: String) -> Option<Self::Value> {
None
}
fn add_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {}
fn set_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {}
}
pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {

View File

@ -331,6 +331,8 @@ declare_features! (
(active, cfg_target_thread_local, "1.7.0", Some(29594), None),
/// Allow conditional compilation depending on rust version
(active, cfg_version, "1.45.0", Some(64796), None),
/// Allows to use the `#[cfi_encoding = ""]` attribute.
(active, cfi_encoding, "1.69.0", Some(89653), None),
/// Allows `for<...>` on closures and generators.
(active, closure_lifetime_binder, "1.64.0", Some(97362), None),
/// Allows `#[track_caller]` on closures and generators.

View File

@ -494,6 +494,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// RFC 2397
gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)),
// `#[cfi_encoding = ""]`
gated!(
cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding,
experimental!(cfi_encoding)
),
// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================

View File

@ -226,6 +226,7 @@ language_item_table! {
PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
// various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.

View File

@ -795,12 +795,16 @@ fn test_unstable_options_tracking_hash() {
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
tracked!(report_delayed_bugs, true);
tracked!(sanitizer, SanitizerSet::ADDRESS);
tracked!(sanitizer_cfi_canonical_jump_tables, None);
tracked!(sanitizer_cfi_generalize_pointers, Some(true));
tracked!(sanitizer_cfi_normalize_integers, Some(true));
tracked!(sanitizer_memory_track_origins, 2);
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
tracked!(saturating_float_casts, Some(true));
tracked!(share_generics, Some(true));
tracked!(show_span, Some(String::from("abc")));
tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
tracked!(split_lto_unit, Some(true));
tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
tracked!(stack_protector, StackProtector::All);
tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));

View File

@ -2400,6 +2400,13 @@ impl<'tcx> Ty<'tcx> {
_ => None,
}
}
pub fn is_c_void(self, tcx: TyCtxt<'_>) -> bool {
match self.kind() {
ty::Adt(adt, _) => tcx.lang_items().get(LangItem::CVoid) == Some(adt.did()),
_ => false,
}
}
}
/// Extra information about why we ended up with a particular variance.

View File

@ -35,7 +35,15 @@ session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible
session_cannot_enable_crt_static_linux = sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static`
session_sanitizer_cfi_enabled = `-Zsanitizer=cfi` requires `-Clto`
session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`
session_sanitizer_cfi_canonical_jump_tables_requires_cfi = `-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi`
session_sanitizer_cfi_generalize_pointers_requires_cfi = `-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`
session_sanitizer_cfi_normalize_integers_requires_cfi = `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`
session_split_lto_unit_requires_lto = `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`
session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto`

View File

@ -1036,6 +1036,14 @@ fn default_configuration(sess: &Session) -> CrateConfig {
ret.insert((sym::sanitize, Some(symbol)));
}
if sess.is_sanitizer_cfi_generalize_pointers_enabled() {
ret.insert((sym::sanitizer_cfi_generalize_pointers, None));
}
if sess.is_sanitizer_cfi_normalize_integers_enabled() {
ret.insert((sym::sanitizer_cfi_normalize_integers, None));
}
if sess.opts.debug_assertions {
ret.insert((sym::debug_assertions, None));
}

View File

@ -111,8 +111,24 @@ pub struct CannotMixAndMatchSanitizers {
pub struct CannotEnableCrtStaticLinux;
#[derive(Diagnostic)]
#[diag(session_sanitizer_cfi_enabled)]
pub struct SanitizerCfiEnabled;
#[diag(session_sanitizer_cfi_requires_lto)]
pub struct SanitizerCfiRequiresLto;
#[derive(Diagnostic)]
#[diag(session_sanitizer_cfi_canonical_jump_tables_requires_cfi)]
pub struct SanitizerCfiCanonicalJumpTablesRequiresCfi;
#[derive(Diagnostic)]
#[diag(session_sanitizer_cfi_generalize_pointers_requires_cfi)]
pub struct SanitizerCfiGeneralizePointersRequiresCfi;
#[derive(Diagnostic)]
#[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)]
pub struct SanitizerCfiNormalizeIntegersRequiresCfi;
#[derive(Diagnostic)]
#[diag(session_split_lto_unit_requires_lto)]
pub struct SplitLtoUnitRequiresLto;
#[derive(Diagnostic)]
#[diag(session_unstable_virtual_function_elimination)]

View File

@ -1662,6 +1662,12 @@ options! {
"immediately print bugs registered with `delay_span_bug` (default: no)"),
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
"use a sanitizer"),
sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED],
"enable canonical jump tables (default: yes)"),
sanitizer_cfi_generalize_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED],
"enable generalizing pointer types (default: no)"),
sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED],
"enable normalizing integer types (default: no)"),
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
"enable origins tracking in MemorySanitizer"),
sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
@ -1707,6 +1713,8 @@ options! {
file which is ignored by the linker
`single`: sections which do not require relocation are written into object file but ignored
by the linker"),
split_lto_unit: Option<bool> = (None, parse_opt_bool, [TRACKED],
"enable LTO unit splitting (default: no)"),
src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
#[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")]

View File

@ -766,10 +766,30 @@ impl Session {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
}
pub fn is_sanitizer_cfi_canonical_jump_tables_disabled(&self) -> bool {
self.opts.unstable_opts.sanitizer_cfi_canonical_jump_tables == Some(false)
}
pub fn is_sanitizer_cfi_canonical_jump_tables_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer_cfi_canonical_jump_tables == Some(true)
}
pub fn is_sanitizer_cfi_generalize_pointers_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer_cfi_generalize_pointers == Some(true)
}
pub fn is_sanitizer_cfi_normalize_integers_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer_cfi_normalize_integers == Some(true)
}
pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
}
pub fn is_split_lto_unit_enabled(&self) -> bool {
self.opts.unstable_opts.split_lto_unit == Some(true)
}
/// 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 {
@ -1582,17 +1602,16 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
sess.emit_err(errors::CannotEnableCrtStaticLinux);
}
// LLVM CFI and VFE both require LTO.
if sess.lto() != config::Lto::Fat {
if sess.is_sanitizer_cfi_enabled() {
sess.emit_err(errors::SanitizerCfiEnabled);
}
if sess.opts.unstable_opts.virtual_function_elimination {
sess.emit_err(errors::UnstableVirtualFunctionElimination);
}
// LLVM CFI requires LTO.
if sess.is_sanitizer_cfi_enabled()
&& !(sess.lto() == config::Lto::Fat
|| sess.lto() == config::Lto::Thin
|| sess.opts.cg.linker_plugin_lto.enabled())
{
sess.emit_err(errors::SanitizerCfiRequiresLto);
}
// LLVM CFI and KCFI are mutually exclusive
// LLVM CFI is incompatible with LLVM KCFI.
if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() {
sess.emit_err(errors::CannotMixAndMatchSanitizers {
first: "cfi".to_string(),
@ -1600,6 +1619,43 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
});
}
// Canonical jump tables requires CFI.
if sess.is_sanitizer_cfi_canonical_jump_tables_disabled() {
if !sess.is_sanitizer_cfi_enabled() {
sess.emit_err(errors::SanitizerCfiCanonicalJumpTablesRequiresCfi);
}
}
// LLVM CFI pointer generalization requires CFI or KCFI.
if sess.is_sanitizer_cfi_generalize_pointers_enabled() {
if !(sess.is_sanitizer_cfi_enabled() || sess.is_sanitizer_kcfi_enabled()) {
sess.emit_err(errors::SanitizerCfiGeneralizePointersRequiresCfi);
}
}
// LLVM CFI integer normalization requires CFI or KCFI.
if sess.is_sanitizer_cfi_normalize_integers_enabled() {
if !(sess.is_sanitizer_cfi_enabled() || sess.is_sanitizer_kcfi_enabled()) {
sess.emit_err(errors::SanitizerCfiNormalizeIntegersRequiresCfi);
}
}
// LTO unit splitting requires LTO.
if sess.is_split_lto_unit_enabled()
&& !(sess.lto() == config::Lto::Fat
|| sess.lto() == config::Lto::Thin
|| sess.opts.cg.linker_plugin_lto.enabled())
{
sess.emit_err(errors::SplitLtoUnitRequiresLto);
}
// VFE requires LTO.
if sess.lto() != config::Lto::Fat {
if sess.opts.unstable_opts.virtual_function_elimination {
sess.emit_err(errors::UnstableVirtualFunctionElimination);
}
}
if sess.opts.unstable_opts.stack_protector != StackProtector::None {
if !sess.target.options.supports_stack_protector {
sess.emit_warning(errors::StackProtectorNotSupportedForTarget {

View File

@ -443,6 +443,7 @@ symbols! {
c_str,
c_unwind,
c_variadic,
c_void,
call,
call_mut,
call_once,
@ -470,6 +471,7 @@ symbols! {
cfg_target_vendor,
cfg_version,
cfi,
cfi_encoding,
char,
client,
clippy,
@ -1323,6 +1325,8 @@ symbols! {
s,
safety,
sanitize,
sanitizer_cfi_generalize_pointers,
sanitizer_cfi_normalize_integers,
sanitizer_runtime,
saturating_add,
saturating_sub,

View File

@ -1,42 +1,65 @@
// For more information about type metadata and type metadata identifiers for cross-language LLVM
// CFI support, see Type metadata in the design document in the tracking issue #89653.
/// Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI
/// support.
///
/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
/// see design document in the tracking issue #89653.
use bitflags::bitflags;
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
use rustc_target::abi::call::FnAbi;
use std::hash::Hasher;
use twox_hash::XxHash64;
bitflags! {
/// Options for typeid_for_fnabi and typeid_for_fnsig.
pub struct TypeIdOptions: u32 {
const GENERALIZE_POINTERS = 1;
const GENERALIZE_REPR_C = 2;
const NORMALIZE_INTEGERS = 4;
}
}
mod typeid_itanium_cxx_abi;
use typeid_itanium_cxx_abi::TypeIdOptions;
/// Returns a type metadata identifier for the specified FnAbi.
pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS)
pub fn typeid_for_fnabi<'tcx>(
tcx: TyCtxt<'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
options: TypeIdOptions,
) -> String {
typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options)
}
/// Returns a type metadata identifier for the specified FnSig.
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)
pub fn typeid_for_fnsig<'tcx>(
tcx: TyCtxt<'tcx>,
fn_sig: &FnSig<'tcx>,
options: TypeIdOptions,
) -> String {
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, 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.)
/// Returns a KCFI type metadata identifier for the specified FnAbi.
pub fn kcfi_typeid_for_fnabi<'tcx>(
tcx: TyCtxt<'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
options: TypeIdOptions,
) -> u32 {
// A 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.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, 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.)
/// Returns a KCFI type metadata identifier for the specified FnSig.
pub fn kcfi_typeid_for_fnsig<'tcx>(
tcx: TyCtxt<'tcx>,
fn_sig: &FnSig<'tcx>,
options: TypeIdOptions,
) -> u32 {
// A 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.write(typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, options).as_bytes());
hash.finish() as u32
}

View File

@ -1,14 +1,16 @@
// For more information about type metadata and type metadata identifiers for cross-language LLVM
// CFI support, see Type metadata in the design document in the tracking issue #89653.
// FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective
// builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C"
// calling convention to use this encoding for cross-language LLVM CFI.
use bitflags::bitflags;
/// Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow
/// Integrity (CFI) and cross-language LLVM CFI support.
///
/// Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium
/// C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that
/// are not used across the FFI boundary.
///
/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
/// see design document in the tracking issue #89653.
use core::fmt::Display;
use rustc_data_structures::base_n;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::DiagnosticMessage;
use rustc_hir as hir;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{
@ -16,11 +18,13 @@ use rustc_middle::ty::{
Ty, TyCtxt, UintTy,
};
use rustc_span::def_id::DefId;
use rustc_span::symbol::sym;
use rustc_span::sym;
use rustc_target::abi::call::{Conv, FnAbi};
use rustc_target::spec::abi::Abi;
use std::fmt::Write as _;
use crate::typeid::TypeIdOptions;
/// Type and extended type qualifiers.
#[derive(Eq, Hash, PartialEq)]
enum TyQ {
@ -38,15 +42,6 @@ enum DictKey<'tcx> {
Predicate(ExistentialPredicate<'tcx>),
}
bitflags! {
/// Options for typeid_for_fnabi and typeid_for_fnsig.
pub struct TypeIdOptions: u32 {
const NO_OPTIONS = 0;
const GENERALIZE_POINTERS = 1;
const GENERALIZE_REPR_C = 2;
}
}
/// Options for encode_ty.
type EncodeTyOptions = TypeIdOptions;
@ -91,21 +86,6 @@ fn compress<'tcx>(
}
}
// FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly
// along with other is_c_type methods.
/// Returns whether a `ty::Ty` is `c_void`.
fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind() {
ty::Adt(adt_def, ..) => {
let def_id = adt_def.0.did;
let crate_name = tcx.crate_name(def_id.krate);
tcx.item_name(def_id).as_str() == "c_void"
&& (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc)
}
_ => false,
}
}
/// Encodes a const using the Itanium C++ ABI as a literal argument (see
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
fn encode_const<'tcx>(
@ -448,6 +428,12 @@ fn encode_ty<'tcx>(
match ty.kind() {
// Primitive types
// Rust's bool has the same layout as C17's _Bool, that is, its size and alignment are
// implementation-defined. Any bool can be cast into an integer, taking on the values 1
// (true) or 0 (false).
//
// (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
ty::Bool => {
typeid.push('b');
}
@ -535,9 +521,33 @@ fn encode_ty<'tcx>(
// User-defined types
ty::Adt(adt_def, substs) => {
let mut s = String::new();
let def_id = adt_def.0.did;
if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
// For cross-language CFI support, the encoding must be compatible at the FFI
let def_id = adt_def.did();
if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) {
// Use user-defined CFI encoding for type
if let Some(value_str) = cfi_encoding.value_str() {
if !value_str.to_string().trim().is_empty() {
s.push_str(&value_str.to_string().trim());
} else {
#[allow(
rustc::diagnostic_outside_of_impl,
rustc::untranslatable_diagnostic
)]
tcx.sess
.struct_span_err(
cfi_encoding.span,
DiagnosticMessage::Str(format!(
"invalid `cfi_encoding` for `{:?}`",
ty.kind()
)),
)
.emit();
}
} else {
bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
}
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
} else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
// For cross-language LLVM CFI support, the encoding must be compatible at the FFI
// boundary. For instance:
//
// struct type1 {};
@ -567,8 +577,33 @@ fn encode_ty<'tcx>(
ty::Foreign(def_id) => {
// <length><name>, where <name> is <unscoped-name>
let mut s = String::new();
let name = tcx.item_name(*def_id).to_string();
let _ = write!(s, "{}{}", name.len(), &name);
if let Some(cfi_encoding) = tcx.get_attr(*def_id, sym::cfi_encoding) {
// Use user-defined CFI encoding for type
if let Some(value_str) = cfi_encoding.value_str() {
if !value_str.to_string().trim().is_empty() {
s.push_str(&value_str.to_string().trim());
} else {
#[allow(
rustc::diagnostic_outside_of_impl,
rustc::untranslatable_diagnostic
)]
tcx.sess
.struct_span_err(
cfi_encoding.span,
DiagnosticMessage::Str(format!(
"invalid `cfi_encoding` for `{:?}`",
ty.kind()
)),
)
.emit();
}
} else {
bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
}
} else {
let name = tcx.item_name(*def_id).to_string();
let _ = write!(s, "{}{}", name.len(), &name);
}
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
@ -618,7 +653,7 @@ fn encode_ty<'tcx>(
ty::FnPtr(fn_sig) => {
// PF<return-type><parameter-type1..parameter-typeN>E
let mut s = String::from("P");
s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS));
s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::empty()));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
@ -655,22 +690,59 @@ fn encode_ty<'tcx>(
}
// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all
// c_void types into unit types unconditionally, and generalizes all pointers if
// TransformTyOptions::GENERALIZE_POINTERS option is set.
#[instrument(level = "trace", skip(tcx))]
// c_void types into unit types unconditionally, generalizes pointers if
// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
// TransformTyOptions::NORMALIZE_INTEGERS option is set.
fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
let mut ty = ty;
match ty.kind() {
ty::Bool
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Char
| ty::Str
| ty::Never
| ty::Foreign(..)
| ty::Dynamic(..) => {}
ty::Float(..) | ty::Char | ty::Str | ty::Never | ty::Foreign(..) | ty::Dynamic(..) => {}
ty::Bool => {
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
// Note: on all platforms that Rust's currently supports, its size and alignment are
// 1, and its ABI class is INTEGER - see Rust Layout and ABIs.
//
// (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
//
// Clang represents bool as an 8-bit unsigned integer.
ty = tcx.types.u8;
}
}
ty::Int(..) | ty::Uint(..) => {
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
// Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit wide.
// All platforms we currently support have a C platform, and as a consequence,
// isize/usize are at least 16-bit wide for all of them.
//
// (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.)
match ty.kind() {
ty::Int(IntTy::Isize) => match tcx.sess.target.pointer_width {
16 => ty = tcx.types.i16,
32 => ty = tcx.types.i32,
64 => ty = tcx.types.i64,
128 => ty = tcx.types.i128,
_ => bug!(
"transform_ty: unexpected pointer width `{}`",
tcx.sess.target.pointer_width
),
},
ty::Uint(UintTy::Usize) => match tcx.sess.target.pointer_width {
16 => ty = tcx.types.u16,
32 => ty = tcx.types.u32,
64 => ty = tcx.types.u64,
128 => ty = tcx.types.u128,
_ => bug!(
"transform_ty: unexpected pointer width `{}`",
tcx.sess.target.pointer_width
),
},
_ => (),
}
}
}
_ if ty.is_unit() => {}
@ -688,12 +760,17 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
}
ty::Adt(adt_def, substs) => {
if is_c_void_ty(tcx, ty) {
if ty.is_c_void(tcx) {
ty = tcx.mk_unit();
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
{
ty = tcx.mk_adt(*adt_def, ty::List::empty());
} else if adt_def.repr().transparent() && adt_def.is_struct() {
// Don't transform repr(transparent) types with an user-defined CFI encoding to
// preserve the user-defined CFI encoding.
if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
return ty;
}
let variant = adt_def.non_enum_variant();
let param_env = tcx.param_env(variant.def_id);
let field = variant.fields.iter().find(|field| {
@ -815,7 +892,7 @@ fn transform_substs<'tcx>(
options: TransformTyOptions,
) -> SubstsRef<'tcx> {
let substs = substs.iter().map(|subst| match subst.unpack() {
GenericArgKind::Type(ty) if is_c_void_ty(tcx, ty) => tcx.mk_unit().into(),
GenericArgKind::Type(ty) if ty.is_c_void(tcx) => tcx.mk_unit().into(),
GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(),
_ => subst,
});
@ -887,6 +964,15 @@ pub fn typeid_for_fnabi<'tcx>(
// Close the "F..E" pair
typeid.push('E');
// Add encoding suffixes
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
typeid.push_str(".normalized");
}
if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
typeid.push_str(".generalized");
}
typeid
}
@ -913,5 +999,14 @@ pub fn typeid_for_fnsig<'tcx>(
// Encode the function signature
typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options));
// Add encoding suffixes
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
typeid.push_str(".normalized");
}
if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
typeid.push_str(".generalized");
}
typeid
}

View File

@ -202,6 +202,7 @@ mod c_long_definition {
// would be uninhabited and at least dereferencing such pointers would
// be UB.
#[doc = include_str!("c_void.md")]
#[cfg_attr(not(bootstrap), lang = "c_void")]
#[repr(u8)]
#[stable(feature = "core_c_void", since = "1.30.0")]
pub enum c_void {

View File

@ -196,18 +196,18 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
# ControlFlowIntegrity
The LLVM Control Flow Integrity (CFI) support in the Rust compiler initially
provides forward-edge control flow protection for Rust-compiled code only by
aggregating function pointers in groups identified by their return and parameter
types.
The LLVM Control Flow Integrity (CFI) support in the Rust compiler provides
forward-edge control flow protection for both Rust-compiled code only and for C
or C++ and Rust -compiled code mixed-language binaries, also known as “mixed
binaries” (i.e., for when C or C++ and Rust -compiled code share the same
virtual address space), by aggregating function pointers in groups identified by
their return and parameter types.
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 CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto).
LLVM CFI can be enabled with `-Zsanitizer=cfi` and requires LTO (i.e., `-Clto`).
Cross-language LLVM CFI can be enabled with `-Zsanitizer=cfi`, and requires the
`-Zsanitizer-cfi-normalize-integers` option to be used with Clang
`-fsanitize-cfi-icall-normalize-integers` for normalizing integer types, and
proper (i.e., non-rustc) LTO (i.e., `-Clinker-plugin-lto`).
See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
@ -343,7 +343,7 @@ $
Fig. 5.Build and execution of the modified example with LLVM CFI disabled.
```shell
$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
$ RUSTFLAGS="-Cembed-bitcode=yes -Clto -Zsanitizer=cfi" cargo run --release
Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
Finished release [optimized] target(s) in 3.38s
Running `target/release/rust-cfi-2`
@ -392,7 +392,7 @@ Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
```shell
cargo run --release
$ cargo run --release
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
Finished release [optimized] target(s) in 0.74s
Running `target/release/rust-cfi-3`
@ -404,7 +404,7 @@ $
Fig. 8.Build and execution of the modified example with LLVM CFI disabled.
```shell
$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
$ RUSTFLAGS="-Cembed-bitcode=yes -Clto -Zsanitizer=cfi" cargo run --release
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
Finished release [optimized] target(s) in 3.40s
Running `target/release/rust-cfi-3`
@ -420,8 +420,92 @@ flow using an indirect branch/call to a function with different return and
parameter types than the return type expected and arguments intended/passed in
the call/branch site, the execution is also terminated (see Fig. 9).
[rust-book-ch19-05]: ../../book/ch19-05-advanced-functions-and-closures.html
[rust-book]: ../../book/title-page.html
```ignore (cannot-test-this-because-uses-custom-build)
int
do_twice(int (*fn)(int), int arg) {
return fn(arg) + fn(arg);
}
```
Fig. 10.Example C library.
```ignore (cannot-test-this-because-uses-custom-build)
use std::mem;
#[link(name = "foo")]
extern "C" {
fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32;
}
unsafe extern "C" fn add_one(x: i32) -> i32 {
x + 1
}
unsafe extern "C" fn add_two(x: i64) -> i64 {
x + 2
}
fn main() {
let answer = unsafe { do_twice(add_one, 5) };
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: unsafe extern "C" fn(i32) -> i32 = unsafe {
mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8)
};
let next_answer = unsafe { do_twice(f, 5) };
println!("The next answer is: {}", next_answer);
}
```
Fig. 11.Another modified example from the [Advanced Functions and
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
```shell
$ make
mkdir -p target/debug
clang -I. -Isrc -Wall -flto -fvisibility=hidden -c -emit-llvm src/foo.c -o target/debug/libfoo.bc
llvm-ar rcs target/debug/libfoo.a target/debug/libfoo.bc
RUSTFLAGS="-L./target/debug -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build
Compiling main v0.1.0 (/home/rcvalle/rust-cross-cfi-1)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
$ ./target/debug/main
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
Fig. 12.Build and execution of the modified example with LLVM CFI disabled.
```shell
$ make
mkdir -p target/debug
clang -I. -Isrc -Wall -flto -fvisibility=hidden -fsanitize=cfi -fsanitize-cfi-icall-normalize-integers -c -emit-llvm src/foo.c -o target/debug/libfoo.bc
llvm-ar rcs target/debug/libfoo.a target/debug/libfoo.bc
RUSTFLAGS="-L./target/debug -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers" cargo build
Compiling main v0.1.0 (/home/rcvalle/rust-cross-cfi-1)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
$ ./target/debug/main
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
Fig. 13.Build and execution of the modified example with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to a function with different return and
parameter types than the return type expected and arguments intended/passed in
the call/branch site, even across the FFI boundary and for extern "C" function
types indirectly called (i.e., callbacks/function pointers) across the FFI
boundary, in C or C++ and Rust -compiled code mixed-language binaries, also
known as “mixed binaries” (i.e., for when C or C++ and Rust -compiled code share
the same virtual address space), the execution is also terminated (see Fig. 13).
[rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
[rust-book]: https://doc.rust-lang.org/book/title-page.html
# HWAddressSanitizer

View File

@ -0,0 +1,25 @@
# `cfi_encoding`
The tracking issue for this feature is: [#89653]
[#89653]: https://github.com/rust-lang/rust/issues/89653
------------------------
The `cfi_encoding` feature allows the user to define a CFI encoding for a type.
It allows the user to use a different names for types that otherwise would be
required to have the same name as used in externally defined C functions.
## Examples
```rust
#![feature(cfi_encoding, extern_types)]
#[cfi_encoding = "3Foo"]
pub struct Type1(i32);
extern {
#[cfi_encoding = "3Bar"]
type Type2;
}
```

View File

@ -0,0 +1,10 @@
// Verifies that "EnableSplitLTOUnit" module flag is added.
//
// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsplit-lto-unit
#![crate_type="lib"]
pub fn foo() {
}
// CHECK: !{{[0-9]+}} = !{i32 4, !"EnableSplitLTOUnit", i32 1}

View File

@ -8,4 +8,4 @@
pub fn foo() {
}
// CHECK: !{{[0-9]+}} = !{i32 2, !"CFI Canonical Jump Tables", i32 1}
// CHECK: !{{[0-9]+}} = !{i32 4, !"CFI Canonical Jump Tables", i32 1}

View File

@ -0,0 +1,11 @@
// Verifies that "EnableSplitLTOUnit" module flag is added.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![crate_type="lib"]
pub fn foo() {
}
// CHECK: !{{[0-9]+}} = !{i32 4, !"EnableSplitLTOUnit", i32 1}

View File

@ -0,0 +1,18 @@
// Verifies that pointer type membership tests for indirect calls are omitted.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0
#![crate_type="lib"]
#![feature(no_sanitize)]
#[no_sanitize(cfi)]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: sanitizer_cfi_emit_type_checks_attr_no_sanitize::foo
// CHECK: Function Attrs: {{.*}}
// CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: start:
// CHECK-NEXT: {{%.+}} = call i32 %f(i32 %arg)
// CHECK-NEXT: ret i32 {{%.+}}
f(arg)
}

View File

@ -6,13 +6,12 @@
#![crate_type="lib"]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}}
// CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: start:
// CHECK: [[TT:%.+]] = call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"{{[[:print:]]+}}")
// CHECK-NEXT: br i1 [[TT]], label %type_test.pass, label %type_test.fail
// CHECK: type_test.pass:
// CHECK-NEXT: {{%.+}} = call i32 %f(i32 %arg)
// CHECK-NEXT: br label %bb1
// CHECK: type_test.fail:
// CHECK-NEXT: call void @llvm.trap()
// CHECK-NEXT: unreachable

View File

@ -0,0 +1,48 @@
// Verifies that user-defined CFI encoding for types are emitted.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![crate_type="lib"]
#![feature(cfi_encoding, extern_types)]
#[cfi_encoding = "3Foo"]
pub struct Type1(i32);
extern {
#[cfi_encoding = "3Bar"]
type Type2;
}
#[cfi_encoding = "3Baz"]
#[repr(transparent)]
pub struct Type3(i32);
pub fn foo0(_: Type1) { }
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo1(_: Type1, _: Type1) { }
// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo2(_: Type1, _: Type1, _: Type1) { }
// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo3(_: *mut Type2) { }
// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo4(_: *mut Type2, _: *mut Type2) { }
// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo5(_: *mut Type2, _: *mut Type2, _: *mut Type2) { }
// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo6(_: *mut Type3) { }
// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo7(_: *mut Type3, _: *mut Type3) { }
// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo8(_: *mut Type3, _: *mut Type3, _: *mut Type3) { }
// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFv3FooE"}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFv3FooS_E"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFv3FooS_S_E"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvP3BarE"}
// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvP3BarS0_E"}
// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvP3BarS0_S0_E"}
// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvP3BazE"}
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvP3BazS0_E"}
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvP3BazS0_S0_E"}

View File

@ -10,7 +10,7 @@
#![feature(adt_const_params, extern_types, inline_const, type_alias_impl_trait)]
extern crate core;
use core::ffi::c_void;
use core::ffi::*;
use std::marker::PhantomData;
// User-defined type (structure)
@ -113,9 +113,10 @@ pub fn fn1<'a>() {
let _: Type11 = Quuux;
}
// repr(transparent) user-defined type
// Helper type to make Type12 have an unique id
struct Foo(i32);
// repr(transparent) user-defined type
#[repr(transparent)]
pub struct Type12 {
member1: (),
@ -131,313 +132,313 @@ pub struct Type13<'a> {
member3: &'a Type13<'a>,
}
// Helper type to allow `Type14<Bar>` to be a unique ID
// Helper type to make Type14 have an unique id
pub struct Bar;
// repr(transparent) parameterized type
// repr(transparent) user-defined generic type
#[repr(transparent)]
pub struct Type14<T>(T);
pub fn foo0(_: ()) { }
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]]
pub fn foo1(_: c_void, _: ()) { }
// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]]
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo1(_: (), _: c_void) { }
// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo2(_: (), _: c_void, _: c_void) { }
// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]]
pub fn foo3(_: *mut c_void) { }
// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]]
pub fn foo4(_: *mut c_void, _: *mut ()) { }
// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]]
// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo3(_: *mut ()) { }
// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo4(_: *mut (), _: *mut c_void) { }
// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo5(_: *mut (), _: *mut c_void, _: *mut c_void) { }
// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]]
pub fn foo6(_: *const c_void) { }
// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]]
pub fn foo7(_: *const c_void, _: *const ()) { }
// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]]
// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo6(_: *const ()) { }
// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo7(_: *const (), _: *const c_void) { }
// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo8(_: *const (), _: *const c_void, _: *const c_void) { }
// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]]
// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo9(_: bool) { }
// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]]
// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo10(_: bool, _: bool) { }
// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]]
// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo11(_: bool, _: bool, _: bool) { }
// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]]
// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo12(_: i8) { }
// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]]
// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo13(_: i8, _: i8) { }
// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]]
// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo14(_: i8, _: i8, _: i8) { }
// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]]
// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo15(_: i16) { }
// CHECK: define{{.*}}foo15{{.*}}!type ![[TYPE15:[0-9]+]]
// CHECK: define{{.*}}foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo16(_: i16, _: i16) { }
// CHECK: define{{.*}}foo16{{.*}}!type ![[TYPE16:[0-9]+]]
// CHECK: define{{.*}}foo16{{.*}}!type ![[TYPE16:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo17(_: i16, _: i16, _: i16) { }
// CHECK: define{{.*}}foo17{{.*}}!type ![[TYPE17:[0-9]+]]
// CHECK: define{{.*}}foo17{{.*}}!type ![[TYPE17:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo18(_: i32) { }
// CHECK: define{{.*}}foo18{{.*}}!type ![[TYPE18:[0-9]+]]
// CHECK: define{{.*}}foo18{{.*}}!type ![[TYPE18:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo19(_: i32, _: i32) { }
// CHECK: define{{.*}}foo19{{.*}}!type ![[TYPE19:[0-9]+]]
// CHECK: define{{.*}}foo19{{.*}}!type ![[TYPE19:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo20(_: i32, _: i32, _: i32) { }
// CHECK: define{{.*}}foo20{{.*}}!type ![[TYPE20:[0-9]+]]
// CHECK: define{{.*}}foo20{{.*}}!type ![[TYPE20:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo21(_: i64) { }
// CHECK: define{{.*}}foo21{{.*}}!type ![[TYPE21:[0-9]+]]
// CHECK: define{{.*}}foo21{{.*}}!type ![[TYPE21:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo22(_: i64, _: i64) { }
// CHECK: define{{.*}}foo22{{.*}}!type ![[TYPE22:[0-9]+]]
// CHECK: define{{.*}}foo22{{.*}}!type ![[TYPE22:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo23(_: i64, _: i64, _: i64) { }
// CHECK: define{{.*}}foo23{{.*}}!type ![[TYPE23:[0-9]+]]
// CHECK: define{{.*}}foo23{{.*}}!type ![[TYPE23:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo24(_: i128) { }
// CHECK: define{{.*}}foo24{{.*}}!type ![[TYPE24:[0-9]+]]
// CHECK: define{{.*}}foo24{{.*}}!type ![[TYPE24:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo25(_: i128, _: i128) { }
// CHECK: define{{.*}}foo25{{.*}}!type ![[TYPE25:[0-9]+]]
// CHECK: define{{.*}}foo25{{.*}}!type ![[TYPE25:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo26(_: i128, _: i128, _: i128) { }
// CHECK: define{{.*}}foo26{{.*}}!type ![[TYPE26:[0-9]+]]
// CHECK: define{{.*}}foo26{{.*}}!type ![[TYPE26:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo27(_: isize) { }
// CHECK: define{{.*}}foo27{{.*}}!type ![[TYPE27:[0-9]+]]
// CHECK: define{{.*}}foo27{{.*}}!type ![[TYPE27:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo28(_: isize, _: isize) { }
// CHECK: define{{.*}}foo28{{.*}}!type ![[TYPE28:[0-9]+]]
// CHECK: define{{.*}}foo28{{.*}}!type ![[TYPE28:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo29(_: isize, _: isize, _: isize) { }
// CHECK: define{{.*}}foo29{{.*}}!type ![[TYPE29:[0-9]+]]
// CHECK: define{{.*}}foo29{{.*}}!type ![[TYPE29:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo30(_: u8) { }
// CHECK: define{{.*}}foo30{{.*}}!type ![[TYPE30:[0-9]+]]
// CHECK: define{{.*}}foo30{{.*}}!type ![[TYPE30:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo31(_: u8, _: u8) { }
// CHECK: define{{.*}}foo31{{.*}}!type ![[TYPE31:[0-9]+]]
// CHECK: define{{.*}}foo31{{.*}}!type ![[TYPE31:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo32(_: u8, _: u8, _: u8) { }
// CHECK: define{{.*}}foo32{{.*}}!type ![[TYPE32:[0-9]+]]
// CHECK: define{{.*}}foo32{{.*}}!type ![[TYPE32:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo33(_: u16) { }
// CHECK: define{{.*}}foo33{{.*}}!type ![[TYPE33:[0-9]+]]
// CHECK: define{{.*}}foo33{{.*}}!type ![[TYPE33:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo34(_: u16, _: u16) { }
// CHECK: define{{.*}}foo34{{.*}}!type ![[TYPE34:[0-9]+]]
// CHECK: define{{.*}}foo34{{.*}}!type ![[TYPE34:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo35(_: u16, _: u16, _: u16) { }
// CHECK: define{{.*}}foo35{{.*}}!type ![[TYPE35:[0-9]+]]
// CHECK: define{{.*}}foo35{{.*}}!type ![[TYPE35:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo36(_: u32) { }
// CHECK: define{{.*}}foo36{{.*}}!type ![[TYPE36:[0-9]+]]
// CHECK: define{{.*}}foo36{{.*}}!type ![[TYPE36:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo37(_: u32, _: u32) { }
// CHECK: define{{.*}}foo37{{.*}}!type ![[TYPE37:[0-9]+]]
// CHECK: define{{.*}}foo37{{.*}}!type ![[TYPE37:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo38(_: u32, _: u32, _: u32) { }
// CHECK: define{{.*}}foo38{{.*}}!type ![[TYPE38:[0-9]+]]
// CHECK: define{{.*}}foo38{{.*}}!type ![[TYPE38:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo39(_: u64) { }
// CHECK: define{{.*}}foo39{{.*}}!type ![[TYPE39:[0-9]+]]
// CHECK: define{{.*}}foo39{{.*}}!type ![[TYPE39:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo40(_: u64, _: u64) { }
// CHECK: define{{.*}}foo40{{.*}}!type ![[TYPE40:[0-9]+]]
// CHECK: define{{.*}}foo40{{.*}}!type ![[TYPE40:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo41(_: u64, _: u64, _: u64) { }
// CHECK: define{{.*}}foo41{{.*}}!type ![[TYPE41:[0-9]+]]
// CHECK: define{{.*}}foo41{{.*}}!type ![[TYPE41:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo42(_: u128) { }
// CHECK: define{{.*}}foo42{{.*}}!type ![[TYPE42:[0-9]+]]
// CHECK: define{{.*}}foo42{{.*}}!type ![[TYPE42:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo43(_: u128, _: u128) { }
// CHECK: define{{.*}}foo43{{.*}}!type ![[TYPE43:[0-9]+]]
// CHECK: define{{.*}}foo43{{.*}}!type ![[TYPE43:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo44(_: u128, _: u128, _: u128) { }
// CHECK: define{{.*}}foo44{{.*}}!type ![[TYPE44:[0-9]+]]
// CHECK: define{{.*}}foo44{{.*}}!type ![[TYPE44:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo45(_: usize) { }
// CHECK: define{{.*}}foo45{{.*}}!type ![[TYPE45:[0-9]+]]
// CHECK: define{{.*}}foo45{{.*}}!type ![[TYPE45:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo46(_: usize, _: usize) { }
// CHECK: define{{.*}}foo46{{.*}}!type ![[TYPE46:[0-9]+]]
// CHECK: define{{.*}}foo46{{.*}}!type ![[TYPE46:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo47(_: usize, _: usize, _: usize) { }
// CHECK: define{{.*}}foo47{{.*}}!type ![[TYPE47:[0-9]+]]
// CHECK: define{{.*}}foo47{{.*}}!type ![[TYPE47:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo48(_: f32) { }
// CHECK: define{{.*}}foo48{{.*}}!type ![[TYPE48:[0-9]+]]
// CHECK: define{{.*}}foo48{{.*}}!type ![[TYPE48:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo49(_: f32, _: f32) { }
// CHECK: define{{.*}}foo49{{.*}}!type ![[TYPE49:[0-9]+]]
// CHECK: define{{.*}}foo49{{.*}}!type ![[TYPE49:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo50(_: f32, _: f32, _: f32) { }
// CHECK: define{{.*}}foo50{{.*}}!type ![[TYPE50:[0-9]+]]
// CHECK: define{{.*}}foo50{{.*}}!type ![[TYPE50:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo51(_: f64) { }
// CHECK: define{{.*}}foo51{{.*}}!type ![[TYPE51:[0-9]+]]
// CHECK: define{{.*}}foo51{{.*}}!type ![[TYPE51:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo52(_: f64, _: f64) { }
// CHECK: define{{.*}}foo52{{.*}}!type ![[TYPE52:[0-9]+]]
// CHECK: define{{.*}}foo52{{.*}}!type ![[TYPE52:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo53(_: f64, _: f64, _: f64) { }
// CHECK: define{{.*}}foo53{{.*}}!type ![[TYPE53:[0-9]+]]
// CHECK: define{{.*}}foo53{{.*}}!type ![[TYPE53:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo54(_: char) { }
// CHECK: define{{.*}}foo54{{.*}}!type ![[TYPE54:[0-9]+]]
// CHECK: define{{.*}}foo54{{.*}}!type ![[TYPE54:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo55(_: char, _: char) { }
// CHECK: define{{.*}}foo55{{.*}}!type ![[TYPE55:[0-9]+]]
// CHECK: define{{.*}}foo55{{.*}}!type ![[TYPE55:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo56(_: char, _: char, _: char) { }
// CHECK: define{{.*}}foo56{{.*}}!type ![[TYPE56:[0-9]+]]
// CHECK: define{{.*}}foo56{{.*}}!type ![[TYPE56:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo57(_: &str) { }
// CHECK: define{{.*}}foo57{{.*}}!type ![[TYPE57:[0-9]+]]
// CHECK: define{{.*}}foo57{{.*}}!type ![[TYPE57:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo58(_: &str, _: &str) { }
// CHECK: define{{.*}}foo58{{.*}}!type ![[TYPE58:[0-9]+]]
// CHECK: define{{.*}}foo58{{.*}}!type ![[TYPE58:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo59(_: &str, _: &str, _: &str) { }
// CHECK: define{{.*}}foo59{{.*}}!type ![[TYPE59:[0-9]+]]
// CHECK: define{{.*}}foo59{{.*}}!type ![[TYPE59:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo60(_: (i32, i32)) { }
// CHECK: define{{.*}}foo60{{.*}}!type ![[TYPE60:[0-9]+]]
// CHECK: define{{.*}}foo60{{.*}}!type ![[TYPE60:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo61(_: (i32, i32), _: (i32, i32)) { }
// CHECK: define{{.*}}foo61{{.*}}!type ![[TYPE61:[0-9]+]]
// CHECK: define{{.*}}foo61{{.*}}!type ![[TYPE61:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo62(_: (i32, i32), _: (i32, i32), _: (i32, i32)) { }
// CHECK: define{{.*}}foo62{{.*}}!type ![[TYPE62:[0-9]+]]
// CHECK: define{{.*}}foo62{{.*}}!type ![[TYPE62:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo63(_: [i32; 32]) { }
// CHECK: define{{.*}}foo63{{.*}}!type ![[TYPE63:[0-9]+]]
// CHECK: define{{.*}}foo63{{.*}}!type ![[TYPE63:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo64(_: [i32; 32], _: [i32; 32]) { }
// CHECK: define{{.*}}foo64{{.*}}!type ![[TYPE64:[0-9]+]]
// CHECK: define{{.*}}foo64{{.*}}!type ![[TYPE64:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo65(_: [i32; 32], _: [i32; 32], _: [i32; 32]) { }
// CHECK: define{{.*}}foo65{{.*}}!type ![[TYPE65:[0-9]+]]
// CHECK: define{{.*}}foo65{{.*}}!type ![[TYPE65:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo66(_: &[i32]) { }
// CHECK: define{{.*}}foo66{{.*}}!type ![[TYPE66:[0-9]+]]
// CHECK: define{{.*}}foo66{{.*}}!type ![[TYPE66:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo67(_: &[i32], _: &[i32]) { }
// CHECK: define{{.*}}foo67{{.*}}!type ![[TYPE67:[0-9]+]]
// CHECK: define{{.*}}foo67{{.*}}!type ![[TYPE67:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo68(_: &[i32], _: &[i32], _: &[i32]) { }
// CHECK: define{{.*}}foo68{{.*}}!type ![[TYPE68:[0-9]+]]
// CHECK: define{{.*}}foo68{{.*}}!type ![[TYPE68:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo69(_: &Struct1::<i32>) { }
// CHECK: define{{.*}}foo69{{.*}}!type ![[TYPE69:[0-9]+]]
// CHECK: define{{.*}}foo69{{.*}}!type ![[TYPE69:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo70(_: &Struct1::<i32>, _: &Struct1::<i32>) { }
// CHECK: define{{.*}}foo70{{.*}}!type ![[TYPE70:[0-9]+]]
// CHECK: define{{.*}}foo70{{.*}}!type ![[TYPE70:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo71(_: &Struct1::<i32>, _: &Struct1::<i32>, _: &Struct1::<i32>) { }
// CHECK: define{{.*}}foo71{{.*}}!type ![[TYPE71:[0-9]+]]
// CHECK: define{{.*}}foo71{{.*}}!type ![[TYPE71:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo72(_: &Enum1::<i32>) { }
// CHECK: define{{.*}}foo72{{.*}}!type ![[TYPE72:[0-9]+]]
// CHECK: define{{.*}}foo72{{.*}}!type ![[TYPE72:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo73(_: &Enum1::<i32>, _: &Enum1::<i32>) { }
// CHECK: define{{.*}}foo73{{.*}}!type ![[TYPE73:[0-9]+]]
// CHECK: define{{.*}}foo73{{.*}}!type ![[TYPE73:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo74(_: &Enum1::<i32>, _: &Enum1::<i32>, _: &Enum1::<i32>) { }
// CHECK: define{{.*}}foo74{{.*}}!type ![[TYPE74:[0-9]+]]
// CHECK: define{{.*}}foo74{{.*}}!type ![[TYPE74:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo75(_: &Union1::<i32>) { }
// CHECK: define{{.*}}foo75{{.*}}!type ![[TYPE75:[0-9]+]]
// CHECK: define{{.*}}foo75{{.*}}!type ![[TYPE75:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo76(_: &Union1::<i32>, _: &Union1::<i32>) { }
// CHECK: define{{.*}}foo76{{.*}}!type ![[TYPE76:[0-9]+]]
// CHECK: define{{.*}}foo76{{.*}}!type ![[TYPE76:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo77(_: &Union1::<i32>, _: &Union1::<i32>, _: &Union1::<i32>) { }
// CHECK: define{{.*}}foo77{{.*}}!type ![[TYPE77:[0-9]+]]
// CHECK: define{{.*}}foo77{{.*}}!type ![[TYPE77:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo78(_: *mut type1) { }
// CHECK: define{{.*}}foo78{{.*}}!type ![[TYPE78:[0-9]+]]
// CHECK: define{{.*}}foo78{{.*}}!type ![[TYPE78:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo79(_: *mut type1, _: *mut type1) { }
// CHECK: define{{.*}}foo79{{.*}}!type ![[TYPE79:[0-9]+]]
// CHECK: define{{.*}}foo79{{.*}}!type ![[TYPE79:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo80(_: *mut type1, _: *mut type1, _: *mut type1) { }
// CHECK: define{{.*}}foo80{{.*}}!type ![[TYPE80:[0-9]+]]
// CHECK: define{{.*}}foo80{{.*}}!type ![[TYPE80:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo81(_: &mut i32) { }
// CHECK: define{{.*}}foo81{{.*}}!type ![[TYPE81:[0-9]+]]
// CHECK: define{{.*}}foo81{{.*}}!type ![[TYPE81:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo82(_: &mut i32, _: &i32) { }
// CHECK: define{{.*}}foo82{{.*}}!type ![[TYPE82:[0-9]+]]
// CHECK: define{{.*}}foo82{{.*}}!type ![[TYPE82:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo83(_: &mut i32, _: &i32, _: &i32) { }
// CHECK: define{{.*}}foo83{{.*}}!type ![[TYPE83:[0-9]+]]
// CHECK: define{{.*}}foo83{{.*}}!type ![[TYPE83:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo84(_: &i32) { }
// CHECK: define{{.*}}foo84{{.*}}!type ![[TYPE84:[0-9]+]]
// CHECK: define{{.*}}foo84{{.*}}!type ![[TYPE84:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo85(_: &i32, _: &mut i32) { }
// CHECK: define{{.*}}foo85{{.*}}!type ![[TYPE85:[0-9]+]]
// CHECK: define{{.*}}foo85{{.*}}!type ![[TYPE85:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo86(_: &i32, _: &mut i32, _: &mut i32) { }
// CHECK: define{{.*}}foo86{{.*}}!type ![[TYPE86:[0-9]+]]
// CHECK: define{{.*}}foo86{{.*}}!type ![[TYPE86:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo87(_: *mut i32) { }
// CHECK: define{{.*}}foo87{{.*}}!type ![[TYPE87:[0-9]+]]
// CHECK: define{{.*}}foo87{{.*}}!type ![[TYPE87:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo88(_: *mut i32, _: *const i32) { }
// CHECK: define{{.*}}foo88{{.*}}!type ![[TYPE88:[0-9]+]]
// CHECK: define{{.*}}foo88{{.*}}!type ![[TYPE88:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo89(_: *mut i32, _: *const i32, _: *const i32) { }
// CHECK: define{{.*}}foo89{{.*}}!type ![[TYPE89:[0-9]+]]
// CHECK: define{{.*}}foo89{{.*}}!type ![[TYPE89:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo90(_: *const i32) { }
// CHECK: define{{.*}}foo90{{.*}}!type ![[TYPE90:[0-9]+]]
// CHECK: define{{.*}}foo90{{.*}}!type ![[TYPE90:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo91(_: *const i32, _: *mut i32) { }
// CHECK: define{{.*}}foo91{{.*}}!type ![[TYPE91:[0-9]+]]
// CHECK: define{{.*}}foo91{{.*}}!type ![[TYPE91:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo92(_: *const i32, _: *mut i32, _: *mut i32) { }
// CHECK: define{{.*}}foo92{{.*}}!type ![[TYPE92:[0-9]+]]
// CHECK: define{{.*}}foo92{{.*}}!type ![[TYPE92:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo93(_: fn(i32) -> i32) { }
// CHECK: define{{.*}}foo93{{.*}}!type ![[TYPE93:[0-9]+]]
// CHECK: define{{.*}}foo93{{.*}}!type ![[TYPE93:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo94(_: fn(i32) -> i32, _: fn(i32) -> i32) { }
// CHECK: define{{.*}}foo94{{.*}}!type ![[TYPE94:[0-9]+]]
// CHECK: define{{.*}}foo94{{.*}}!type ![[TYPE94:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo95(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) { }
// CHECK: define{{.*}}foo95{{.*}}!type ![[TYPE95:[0-9]+]]
// CHECK: define{{.*}}foo95{{.*}}!type ![[TYPE95:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo96(_: &dyn Fn(i32) -> i32) { }
// CHECK: define{{.*}}foo96{{.*}}!type ![[TYPE96:[0-9]+]]
// CHECK: define{{.*}}foo96{{.*}}!type ![[TYPE96:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo97(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { }
// CHECK: define{{.*}}foo97{{.*}}!type ![[TYPE97:[0-9]+]]
// CHECK: define{{.*}}foo97{{.*}}!type ![[TYPE97:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo98(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { }
// CHECK: define{{.*}}foo98{{.*}}!type ![[TYPE98:[0-9]+]]
// CHECK: define{{.*}}foo98{{.*}}!type ![[TYPE98:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo99(_: &dyn FnMut(i32) -> i32) { }
// CHECK: define{{.*}}foo99{{.*}}!type ![[TYPE99:[0-9]+]]
// CHECK: define{{.*}}foo99{{.*}}!type ![[TYPE99:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo100(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { }
// CHECK: define{{.*}}foo100{{.*}}!type ![[TYPE100:[0-9]+]]
// CHECK: define{{.*}}foo100{{.*}}!type ![[TYPE100:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo101(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { }
// CHECK: define{{.*}}foo101{{.*}}!type ![[TYPE101:[0-9]+]]
// CHECK: define{{.*}}foo101{{.*}}!type ![[TYPE101:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo102(_: &dyn FnOnce(i32) -> i32) { }
// CHECK: define{{.*}}foo102{{.*}}!type ![[TYPE102:[0-9]+]]
// CHECK: define{{.*}}foo102{{.*}}!type ![[TYPE102:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo103(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) { }
// CHECK: define{{.*}}foo103{{.*}}!type ![[TYPE103:[0-9]+]]
// CHECK: define{{.*}}foo103{{.*}}!type ![[TYPE103:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo104(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) {}
// CHECK: define{{.*}}foo104{{.*}}!type ![[TYPE104:[0-9]+]]
// CHECK: define{{.*}}foo104{{.*}}!type ![[TYPE104:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo105(_: &dyn Send) { }
// CHECK: define{{.*}}foo105{{.*}}!type ![[TYPE105:[0-9]+]]
// CHECK: define{{.*}}foo105{{.*}}!type ![[TYPE105:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo106(_: &dyn Send, _: &dyn Send) { }
// CHECK: define{{.*}}foo106{{.*}}!type ![[TYPE106:[0-9]+]]
// CHECK: define{{.*}}foo106{{.*}}!type ![[TYPE106:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo107(_: &dyn Send, _: &dyn Send, _: &dyn Send) { }
// CHECK: define{{.*}}foo107{{.*}}!type ![[TYPE107:[0-9]+]]
// CHECK: define{{.*}}foo107{{.*}}!type ![[TYPE107:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo108(_: Type1) { }
// CHECK: define{{.*}}foo108{{.*}}!type ![[TYPE108:[0-9]+]]
// CHECK: define{{.*}}foo108{{.*}}!type ![[TYPE108:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo109(_: Type1, _: Type1) { }
// CHECK: define{{.*}}foo109{{.*}}!type ![[TYPE109:[0-9]+]]
// CHECK: define{{.*}}foo109{{.*}}!type ![[TYPE109:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo110(_: Type1, _: Type1, _: Type1) { }
// CHECK: define{{.*}}foo110{{.*}}!type ![[TYPE110:[0-9]+]]
// CHECK: define{{.*}}foo110{{.*}}!type ![[TYPE110:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo111(_: Type2) { }
// CHECK: define{{.*}}foo111{{.*}}!type ![[TYPE111:[0-9]+]]
// CHECK: define{{.*}}foo111{{.*}}!type ![[TYPE111:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo112(_: Type2, _: Type2) { }
// CHECK: define{{.*}}foo112{{.*}}!type ![[TYPE112:[0-9]+]]
// CHECK: define{{.*}}foo112{{.*}}!type ![[TYPE112:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo113(_: Type2, _: Type2, _: Type2) { }
// CHECK: define{{.*}}foo113{{.*}}!type ![[TYPE113:[0-9]+]]
// CHECK: define{{.*}}foo113{{.*}}!type ![[TYPE113:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo114(_: Type3) { }
// CHECK: define{{.*}}foo114{{.*}}!type ![[TYPE114:[0-9]+]]
// CHECK: define{{.*}}foo114{{.*}}!type ![[TYPE114:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo115(_: Type3, _: Type3) { }
// CHECK: define{{.*}}foo115{{.*}}!type ![[TYPE115:[0-9]+]]
// CHECK: define{{.*}}foo115{{.*}}!type ![[TYPE115:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo116(_: Type3, _: Type3, _: Type3) { }
// CHECK: define{{.*}}foo116{{.*}}!type ![[TYPE116:[0-9]+]]
// CHECK: define{{.*}}foo116{{.*}}!type ![[TYPE116:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo117(_: Type4) { }
// CHECK: define{{.*}}foo117{{.*}}!type ![[TYPE117:[0-9]+]]
// CHECK: define{{.*}}foo117{{.*}}!type ![[TYPE117:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo118(_: Type4, _: Type4) { }
// CHECK: define{{.*}}foo118{{.*}}!type ![[TYPE118:[0-9]+]]
// CHECK: define{{.*}}foo118{{.*}}!type ![[TYPE118:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo119(_: Type4, _: Type4, _: Type4) { }
// CHECK: define{{.*}}foo119{{.*}}!type ![[TYPE119:[0-9]+]]
// CHECK: define{{.*}}foo119{{.*}}!type ![[TYPE119:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo120(_: Type5) { }
// CHECK: define{{.*}}foo120{{.*}}!type ![[TYPE120:[0-9]+]]
// CHECK: define{{.*}}foo120{{.*}}!type ![[TYPE120:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo121(_: Type5, _: Type5) { }
// CHECK: define{{.*}}foo121{{.*}}!type ![[TYPE121:[0-9]+]]
// CHECK: define{{.*}}foo121{{.*}}!type ![[TYPE121:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo122(_: Type5, _: Type5, _: Type5) { }
// CHECK: define{{.*}}foo122{{.*}}!type ![[TYPE122:[0-9]+]]
// CHECK: define{{.*}}foo122{{.*}}!type ![[TYPE122:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo123(_: Type6) { }
// CHECK: define{{.*}}foo123{{.*}}!type ![[TYPE123:[0-9]+]]
// CHECK: define{{.*}}foo123{{.*}}!type ![[TYPE123:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo124(_: Type6, _: Type6) { }
// CHECK: define{{.*}}foo124{{.*}}!type ![[TYPE124:[0-9]+]]
// CHECK: define{{.*}}foo124{{.*}}!type ![[TYPE124:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo125(_: Type6, _: Type6, _: Type6) { }
// CHECK: define{{.*}}foo125{{.*}}!type ![[TYPE125:[0-9]+]]
// CHECK: define{{.*}}foo125{{.*}}!type ![[TYPE125:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo126(_: Type7) { }
// CHECK: define{{.*}}foo126{{.*}}!type ![[TYPE126:[0-9]+]]
// CHECK: define{{.*}}foo126{{.*}}!type ![[TYPE126:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo127(_: Type7, _: Type7) { }
// CHECK: define{{.*}}foo127{{.*}}!type ![[TYPE127:[0-9]+]]
// CHECK: define{{.*}}foo127{{.*}}!type ![[TYPE127:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo128(_: Type7, _: Type7, _: Type7) { }
// CHECK: define{{.*}}foo128{{.*}}!type ![[TYPE128:[0-9]+]]
// CHECK: define{{.*}}foo128{{.*}}!type ![[TYPE128:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo129(_: Type8) { }
// CHECK: define{{.*}}foo129{{.*}}!type ![[TYPE129:[0-9]+]]
// CHECK: define{{.*}}foo129{{.*}}!type ![[TYPE129:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo130(_: Type8, _: Type8) { }
// CHECK: define{{.*}}foo130{{.*}}!type ![[TYPE130:[0-9]+]]
// CHECK: define{{.*}}foo130{{.*}}!type ![[TYPE130:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo131(_: Type8, _: Type8, _: Type8) { }
// CHECK: define{{.*}}foo131{{.*}}!type ![[TYPE131:[0-9]+]]
// CHECK: define{{.*}}foo131{{.*}}!type ![[TYPE131:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo132(_: Type9) { }
// CHECK: define{{.*}}foo132{{.*}}!type ![[TYPE132:[0-9]+]]
// CHECK: define{{.*}}foo132{{.*}}!type ![[TYPE132:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo133(_: Type9, _: Type9) { }
// CHECK: define{{.*}}foo133{{.*}}!type ![[TYPE133:[0-9]+]]
// CHECK: define{{.*}}foo133{{.*}}!type ![[TYPE133:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo134(_: Type9, _: Type9, _: Type9) { }
// CHECK: define{{.*}}foo134{{.*}}!type ![[TYPE134:[0-9]+]]
// CHECK: define{{.*}}foo134{{.*}}!type ![[TYPE134:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo135(_: Type10) { }
// CHECK: define{{.*}}foo135{{.*}}!type ![[TYPE135:[0-9]+]]
// CHECK: define{{.*}}foo135{{.*}}!type ![[TYPE135:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo136(_: Type10, _: Type10) { }
// CHECK: define{{.*}}foo136{{.*}}!type ![[TYPE136:[0-9]+]]
// CHECK: define{{.*}}foo136{{.*}}!type ![[TYPE136:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo137(_: Type10, _: Type10, _: Type10) { }
// CHECK: define{{.*}}foo137{{.*}}!type ![[TYPE137:[0-9]+]]
// CHECK: define{{.*}}foo137{{.*}}!type ![[TYPE137:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo138(_: Type11) { }
// CHECK: define{{.*}}foo138{{.*}}!type ![[TYPE138:[0-9]+]]
// CHECK: define{{.*}}foo138{{.*}}!type ![[TYPE138:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo139(_: Type11, _: Type11) { }
// CHECK: define{{.*}}foo139{{.*}}!type ![[TYPE139:[0-9]+]]
// CHECK: define{{.*}}foo139{{.*}}!type ![[TYPE139:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo140(_: Type11, _: Type11, _: Type11) { }
// CHECK: define{{.*}}foo140{{.*}}!type ![[TYPE140:[0-9]+]]
// CHECK: define{{.*}}foo140{{.*}}!type ![[TYPE140:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo141(_: Type12) { }
// CHECK: define{{.*}}foo141{{.*}}!type ![[TYPE141:[0-9]+]]
// CHECK: define{{.*}}foo141{{.*}}!type ![[TYPE141:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo142(_: Type12, _: Type12) { }
// CHECK: define{{.*}}foo142{{.*}}!type ![[TYPE142:[0-9]+]]
// CHECK: define{{.*}}foo142{{.*}}!type ![[TYPE142:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo143(_: Type12, _: Type12, _: Type12) { }
// CHECK: define{{.*}}foo143{{.*}}!type ![[TYPE143:[0-9]+]]
// CHECK: define{{.*}}foo143{{.*}}!type ![[TYPE143:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo144(_: Type13) { }
// CHECK: define{{.*}}foo144{{.*}}!type ![[TYPE144:[0-9]+]]
// CHECK: define{{.*}}foo144{{.*}}!type ![[TYPE144:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo145(_: Type13, _: Type13) { }
// CHECK: define{{.*}}foo145{{.*}}!type ![[TYPE145:[0-9]+]]
// CHECK: define{{.*}}foo145{{.*}}!type ![[TYPE145:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo146(_: Type13, _: Type13, _: Type13) { }
// CHECK: define{{.*}}foo146{{.*}}!type ![[TYPE146:[0-9]+]]
// CHECK: define{{.*}}foo146{{.*}}!type ![[TYPE146:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo147(_: Type14<Bar>) { }
// CHECK: define{{.*}}foo147{{.*}}!type ![[TYPE147:[0-9]+]]
// CHECK: define{{.*}}foo147{{.*}}!type ![[TYPE147:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo148(_: Type14<Bar>, _: Type14<Bar>) { }
// CHECK: define{{.*}}foo148{{.*}}!type ![[TYPE148:[0-9]+]]
// CHECK: define{{.*}}foo148{{.*}}!type ![[TYPE148:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo149(_: Type14<Bar>, _: Type14<Bar>, _: Type14<Bar>) { }
// CHECK: define{{.*}}foo149{{.*}}!type ![[TYPE149:[0-9]+]]
// CHECK: define{{.*}}foo149{{.*}}!type ![[TYPE149:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvvE"}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvvE"}

View File

@ -0,0 +1,31 @@
// Verifies that generalized type metadata for functions are emitted.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers
#![crate_type="lib"]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo
// CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_E.generalized")
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E.generalized")
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E.generalized")
f(arg1, arg2, arg3)
}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFu3i32PKvS_E.generalized"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFu3i32PKvS_S_E.generalized"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFu3i32PKvS_S_S_E.generalized"}

View File

@ -0,0 +1,31 @@
// Verifies that normalized and generalized type metadata for functions are emitted.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers
#![crate_type="lib"]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo
// CHECK-SAME: {{.*}}![[TYPE1:[0-9]+]]
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_E.normalized.generalized")
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}![[TYPE2:[0-9]+]]
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E.normalized.generalized")
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}![[TYPE3:[0-9]+]]
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E.normalized.generalized")
f(arg1, arg2, arg3)
}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFu3i32PKvS_E.normalized.generalized"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFu3i32PKvS_S_E.normalized.generalized"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFu3i32PKvS_S_S_E.normalized.generalized"}

View File

@ -0,0 +1,31 @@
// Verifies that normalized type metadata for functions are emitted.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers
#![crate_type="lib"]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo
// CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_E.normalized")
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E.normalized")
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E.normalized")
f(arg1, arg2, arg3)
}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFu3i32PFS_S_ES_E.normalized"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFu3i32PFS_S_S_ES_S_E.normalized"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFu3i32PFS_S_S_S_ES_S_S_E.normalized"}

View File

@ -7,21 +7,21 @@
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo
// CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]]
// CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_E")
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]]
// CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E")
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]]
// CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E")
f(arg1, arg2, arg3)
}

View File

@ -0,0 +1,46 @@
// Verifies that pointer types are generalized.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers
#![crate_type="lib"]
extern crate core;
pub fn foo0(_: &mut i32) { }
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo1(_: &mut i32, _: &mut i32) { }
// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo2(_: &mut i32, _: &mut i32, _: &mut i32) { }
// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo3(_: &i32) { }
// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo4(_: &i32, _: &i32) { }
// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo5(_: &i32, _: &i32, _: &i32) { }
// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo6(_: *mut i32) { }
// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo7(_: *mut i32, _: *mut i32) { }
// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo8(_: *mut i32, _: *mut i32, _: *mut i32) { }
// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo9(_: *const i32) { }
// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo10(_: *const i32, _: *const i32) { }
// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo11(_: *const i32, _: *const i32, _: *const i32) { }
// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}}
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvU3mutu3refIvEE.generalized"}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvU3mutu3refIvES0_E.generalized"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvU3mutu3refIvES0_S0_E.generalized"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIvEE.generalized"}
// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIvES_E.generalized"}
// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIvES_S_E.generalized"}
// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvPvE.generalized"}
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvPvS_E.generalized"}
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvPvS_S_E.generalized"}
// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvPKvE.generalized"}
// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvPKvS0_E.generalized"}
// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvPKvS0_S0_E.generalized"}

View File

@ -0,0 +1,83 @@
// Verifies that integer types are normalized.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers
#![crate_type="lib"]
extern crate core;
use core::ffi::*;
pub fn foo0(_: bool) { }
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}}
pub fn foo1(_: bool, _: c_uchar) { }
// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}}
pub fn foo2(_: bool, _: c_uchar, _: c_uchar) { }
// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}}
pub fn foo3(_: isize) { }
// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}}
pub fn foo4(_: isize, _: c_long) { }
// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}}
pub fn foo5(_: isize, _: c_long, _: c_longlong) { }
// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}}
pub fn foo6(_: usize) { }
// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}}
pub fn foo7(_: usize, _: c_ulong) { }
// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}}
pub fn foo8(_: usize, _: c_ulong, _: c_ulonglong) { }
// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}}
pub fn foo9(_: c_schar) { }
// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}}
pub fn foo10(_: c_char, _: c_schar) { }
// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}}
pub fn foo11(_: c_char, _: c_schar, _: c_schar) { }
// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}}
pub fn foo12(_: c_int) { }
// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}}
pub fn foo13(_: c_int, _: c_int) { }
// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}}
pub fn foo14(_: c_int, _: c_int, _: c_int) { }
// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}}
pub fn foo15(_: c_short) { }
// CHECK: define{{.*}}foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}}
pub fn foo16(_: c_short, _: c_short) { }
// CHECK: define{{.*}}foo16{{.*}}!type ![[TYPE16:[0-9]+]] !type !{{[0-9]+}}
pub fn foo17(_: c_short, _: c_short, _: c_short) { }
// CHECK: define{{.*}}foo17{{.*}}!type ![[TYPE17:[0-9]+]] !type !{{[0-9]+}}
pub fn foo18(_: c_uint) { }
// CHECK: define{{.*}}foo18{{.*}}!type ![[TYPE18:[0-9]+]] !type !{{[0-9]+}}
pub fn foo19(_: c_uint, _: c_uint) { }
// CHECK: define{{.*}}foo19{{.*}}!type ![[TYPE19:[0-9]+]] !type !{{[0-9]+}}
pub fn foo20(_: c_uint, _: c_uint, _: c_uint) { }
// CHECK: define{{.*}}foo20{{.*}}!type ![[TYPE20:[0-9]+]] !type !{{[0-9]+}}
pub fn foo21(_: c_ushort) { }
// CHECK: define{{.*}}foo21{{.*}}!type ![[TYPE21:[0-9]+]] !type !{{[0-9]+}}
pub fn foo22(_: c_ushort, _: c_ushort) { }
// CHECK: define{{.*}}foo22{{.*}}!type ![[TYPE22:[0-9]+]] !type !{{[0-9]+}}
pub fn foo23(_: c_ushort, _: c_ushort, _: c_ushort) { }
// CHECK: define{{.*}}foo23{{.*}}!type ![[TYPE23:[0-9]+]] !type !{{[0-9]+}}
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvu2u8E.normalized"}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu2u8S_E.normalized"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu2u8S_S_E.normalized"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64}}E.normalized"}
// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64}}{{u3i32|u3i64|S_}}E.normalized"}
// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64}}{{u3i32|u3i64|S_}}{{u3i64|S_|S0_}}E.normalized"}
// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64}}E.normalized"}
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64}}{{u3u32|u3u64|S_}}E.normalized"}
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64}}{{u3u32|u3u64|S_}}{{u3u64|S_|S0_}}E.normalized"}
// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu2i8E.normalized"}
// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFv{{u2i8S_|u2u8u2i8}}E.normalized"}
// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFv{{u2i8S_S_|u2u8u2i8S0_}}E.normalized"}
// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64}}E.normalized"}
// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64}}S_E.normalized"}
// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64}}S_S_E.normalized"}
// CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvu3i16E.normalized"}
// CHECK: ![[TYPE16]] = !{i64 0, !"_ZTSFvu3i16S_E.normalized"}
// CHECK: ![[TYPE17]] = !{i64 0, !"_ZTSFvu3i16S_S_E.normalized"}
// CHECK: ![[TYPE18]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64}}E.normalized"}
// CHECK: ![[TYPE19]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64}}S_E.normalized"}
// CHECK: ![[TYPE20]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64}}S_S_E.normalized"}
// CHECK: ![[TYPE21]] = !{i64 0, !"_ZTSFvu3u16E.normalized"}
// CHECK: ![[TYPE22]] = !{i64 0, !"_ZTSFvu3u16S_E.normalized"}
// CHECK: ![[TYPE23]] = !{i64 0, !"_ZTSFvu3u16S_S_E.normalized"}

View File

@ -0,0 +1,30 @@
// Verifies that KCFI operand bundles are omitted.
//
// 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 -Copt-level=0
#![crate_type="lib"]
#![feature(no_core, no_sanitize, lang_items)]
#![no_core]
#[lang="sized"]
trait Sized { }
#[lang="copy"]
trait Copy { }
impl Copy for i32 {}
#[no_sanitize(kcfi)]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: sanitizer_kcfi_emit_kcfi_operand_bundle_attr_no_sanitize::foo
// CHECK: Function Attrs: {{.*}}
// CHECK-LABEL: define{{.*}}foo{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: start:
// CHECK-NOT: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 {{[-0-9]+}}) ]
// CHECK: ret i32 {{%.+}}
f(arg)
}

View File

@ -0,0 +1,44 @@
// Verifies that generalized 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 -Zsanitizer-cfi-generalize-pointers
#![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
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE1:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 233085384) ]
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE2:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%arg2){{.*}}[ "kcfi"(i32 435418021) ]
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE3:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%arg2, i32 {{(noundef )*}}%arg3){{.*}}[ "kcfi"(i32 -1003721339) ]
f(arg1, arg2, arg3)
}
// CHECK: ![[TYPE1]] = !{i32 -1741689296}
// CHECK: ![[TYPE2]] = !{i32 489439372}
// CHECK: ![[TYPE3]] = !{i32 2026563871}

View File

@ -0,0 +1,44 @@
// Verifies that normalized and generalized 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 -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers
#![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
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE1:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 -686570305) ]
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE2:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%arg2){{.*}}[ "kcfi"(i32 1281038450) ]
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE3:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%arg2, i32 {{(noundef )*}}%arg3){{.*}}[ "kcfi"(i32 -1751512973) ]
f(arg1, arg2, arg3)
}
// CHECK: ![[TYPE1]] = !{i32 975484707}
// CHECK: ![[TYPE2]] = !{i32 1658833102}
// CHECK: ![[TYPE3]] = !{i32 230429758}

View File

@ -0,0 +1,44 @@
// Verifies that normalized 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 -Zsanitizer-cfi-normalize-integers
#![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
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE1:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 -841055669) ]
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE2:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%arg2){{.*}}[ "kcfi"(i32 1390819368) ]
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE3:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%arg2, i32 {{(noundef )*}}%arg3){{.*}}[ "kcfi"(i32 586925835) ]
f(arg1, arg2, arg3)
}
// CHECK: ![[TYPE1]] = !{i32 -458317079}
// CHECK: ![[TYPE2]] = !{i32 1737138182}
// CHECK: ![[TYPE3]] = !{i32 197182412}

View File

@ -20,22 +20,22 @@ impl Copy for i32 {}
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE1:[0-9]+]]
// CHECK: call i32 %f(i32 %arg){{.*}}[ "kcfi"(i32 -1666898348) ]
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE1:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 -1666898348) ]
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE2:[0-9]+]]
// CHECK: call i32 %f(i32 %arg1, i32 %arg2){{.*}}[ "kcfi"(i32 -1789026986) ]
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE2:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%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
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE3:[0-9]+]]
// CHECK: call i32 %f(i32 %arg1, i32 %arg2, i32 %arg3){{.*}}[ "kcfi"(i32 1248878270) ]
// CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE3:[0-9]+]]
// CHECK: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg1, i32 {{(noundef )*}}%arg2, i32 {{(noundef )*}}%arg3){{.*}}[ "kcfi"(i32 1248878270) ]
f(arg1, arg2, arg3)
}

View File

@ -0,0 +1,27 @@
// Verifies that KCFI operand bundles 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 -Copt-level=0
#![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{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: start:
// CHECK-NEXT: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 {{[-0-9]+}}) ]
// CHECK-NEXT: ret i32 {{%.+}}
f(arg)
}

View File

@ -0,0 +1,11 @@
// Verifies that "EnableSplitLTOUnit" module flag is added.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsplit-lto-unit
#![crate_type="lib"]
pub fn foo() {
}
// CHECK: !{{[0-9]+}} = !{i32 4, !"EnableSplitLTOUnit", i32 1}

View File

@ -0,0 +1,4 @@
#![crate_type = "lib"]
#[cfi_encoding = "3Bar"] //~ERROR 3:1: 3:25: the `#[cfi_encoding]` attribute is an experimental feature [E0658]
pub struct Foo(i32);

View File

@ -0,0 +1,12 @@
error[E0658]: the `#[cfi_encoding]` attribute is an experimental feature
--> $DIR/feature-gate-cfi_encoding.rs:3:1
|
LL | #[cfi_encoding = "3Bar"]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #89653 <https://github.com/rust-lang/rust/issues/89653> for more information
= help: add `#![feature(cfi_encoding)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -3,12 +3,16 @@
// needs-sanitizer-support
// needs-sanitizer-address
// needs-sanitizer-cfi
// needs-sanitizer-kcfi
// needs-sanitizer-leak
// needs-sanitizer-memory
// needs-sanitizer-thread
// check-pass
// revisions: address leak memory thread
//[address]compile-flags: -Zsanitizer=address --cfg address
//[cfi]compile-flags: -Zsanitizer=cfi --cfg cfi
//[kcfi]compile-flags: -Zsanitizer=kcfi --cfg kcfi
//[leak]compile-flags: -Zsanitizer=leak --cfg leak
//[memory]compile-flags: -Zsanitizer=memory --cfg memory
//[thread]compile-flags: -Zsanitizer=thread --cfg thread
@ -18,6 +22,12 @@
#[cfg(all(sanitize = "address", address))]
fn main() {}
#[cfg(all(sanitize = "cfi", cfi))]
fn main() {}
#[cfg(all(sanitize = "kcfi", kcfi))]
fn main() {}
#[cfg(all(sanitize = "leak", leak))]
fn main() {}

View File

@ -0,0 +1,8 @@
// Verifies that `-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi`.
//
// needs-sanitizer-cfi
// compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-canonical-jump-tables=false
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi`
error: aborting due to previous error

View File

@ -0,0 +1,9 @@
// Verifies that when compiling with `-Zsanitizer-cfi-generalize-pointers` the
// `#[cfg(sanitizer_cfi_generalize_pointers)]` attribute is configured.
//
// needs-sanitizer-cfi
// check-pass
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers
#[cfg(sanitizer_cfi_generalize_pointers)]
fn main() {}

View File

@ -0,0 +1,9 @@
// Verifies that `-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or
// `-Zsanitizer=kcfi`.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-generalize-pointers
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`
error: aborting due to previous error

View File

@ -0,0 +1,11 @@
// Verifies that invalid user-defined CFI encodings can't be used.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![feature(cfi_encoding, no_core)]
#![no_core]
#![no_main]
#[cfi_encoding] //~ERROR 10:1: 10:16: malformed `cfi_encoding` attribute input
pub struct Type1(i32);

View File

@ -0,0 +1,8 @@
error: malformed `cfi_encoding` attribute input
--> $DIR/sanitizer-cfi-invalid-attr-cfi-encoding.rs:10:1
|
LL | #[cfi_encoding]
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[cfi_encoding = "encoding"]`
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
error: cfi sanitizer is not supported for this target
error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi`
error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi`
error: aborting due to 3 previous errors

View File

@ -0,0 +1,12 @@
// Verifies that `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi`.
//
// 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: x86
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer=kcfi
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,8 @@
error: cfi sanitizer is not supported for this target
error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi`
error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi`
error: aborting due to 3 previous errors

View File

@ -0,0 +1,9 @@
// Verifies that when compiling with `-Zsanitizer-cfi-normalize-integers` the
// `#[cfg(sanitizer_cfi_normalize_integers)]` attribute is configured.
//
// needs-sanitizer-cfi
// check-pass
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers
#[cfg(sanitizer_cfi_normalize_integers)]
fn main() {}

View File

@ -0,0 +1,9 @@
// Verifies that `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or
// `-Zsanitizer=kcfi`
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-normalize-integers
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
// Verifies that `-Zsanitizer=cfi` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`.
//
// needs-sanitizer-cfi
// compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsanitizer=cfi` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
// Verifies that `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`.
//
// needs-sanitizer-cfi
// compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsplit-lto-unit
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`
error: aborting due to previous error