diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 4d18818bbe7bd..3223d3f73bdc2 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -595,6 +595,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { llfn, &cx.tcx.codegen_instance_attrs(instance.def), Some(instance), + cx.sanitizer_ignorelist.as_ref(), ); } } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 9bace9d2acf61..bf4ad2522fb7d 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -125,7 +125,7 @@ fn create_wrapper_function( ty, ); - llfn_attrs_from_instance(cx, tcx, llfn, attrs, None); + llfn_attrs_from_instance(cx, tcx, llfn, attrs, None, None); let no_return = if no_return { // -> ! DIFlagNoReturn diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 6295d05f5d508..55ba4fe138b17 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -111,9 +111,9 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>( cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>, sanitizer_fn_attr: SanitizerFnAttrs, + enabled: SanitizerSet, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - let enabled = tcx.sess.sanitizers() - sanitizer_fn_attr.disabled; if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); } @@ -409,6 +409,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( llfn: &'ll Value, codegen_fn_attrs: &CodegenFnAttrs, instance: Option>, + sanitizer_ignorelist: Option<&crate::llvm::SanitizerIgnoreList>, ) { let sess = tcx.sess; let mut to_add = SmallVec::<[_; 16]>::new(); @@ -470,7 +471,67 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // not used. } else { // Do not set sanitizer attributes for naked functions. - to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers)); + let mut enabled = tcx.sess.sanitizers() - codegen_fn_attrs.sanitizers.disabled; + if let Some(ignorelist) = sanitizer_ignorelist { + if let Some(instance) = instance { + let sym_name = tcx.symbol_name(instance).name; + let span = tcx.def_span(instance.def_id()); + let source_map = tcx.sess.source_map(); + let filename = + source_map.span_to_filename(span).prefer_local_unconditionally().to_string(); + + let mainfile = tcx + .sess + .local_crate_source_file() + .and_then(|path| path.local_path().map(|p| p.display().to_string())) + .unwrap_or_default(); + + let demangled = rustc_middle::ty::print::with_no_trimmed_paths!( + tcx.def_path_str(instance.def_id()) + ); + let is_ignored = |section: &std::ffi::CStr| -> bool { + ignorelist.contains_prefix(section, c"fun", sym_name) + || ignorelist.contains_prefix(section, c"fun", &demangled) + || ignorelist.contains_prefix(section, c"src", &filename) + || (!mainfile.is_empty() + && ignorelist.contains_prefix(section, c"mainfile", &mainfile)) + }; + + let ignore_address = is_ignored(c"address"); + let ignore_kernel_address = ignore_address || is_ignored(c"kernel-address"); + let ignore_hwaddress = is_ignored(c"hwaddress"); + let ignore_kernel_hwaddress = ignore_hwaddress || is_ignored(c"kernel-hwaddress"); + + if enabled.contains(SanitizerSet::ADDRESS) && ignore_address { + enabled.remove(SanitizerSet::ADDRESS); + } + if enabled.contains(SanitizerSet::KERNELADDRESS) && ignore_kernel_address { + enabled.remove(SanitizerSet::KERNELADDRESS); + } + if enabled.contains(SanitizerSet::MEMORY) && is_ignored(c"memory") { + enabled.remove(SanitizerSet::MEMORY); + } + if enabled.contains(SanitizerSet::THREAD) && is_ignored(c"thread") { + enabled.remove(SanitizerSet::THREAD); + } + if enabled.contains(SanitizerSet::HWADDRESS) && ignore_hwaddress { + enabled.remove(SanitizerSet::HWADDRESS); + } + if enabled.contains(SanitizerSet::KERNELHWADDRESS) && ignore_kernel_hwaddress { + enabled.remove(SanitizerSet::KERNELHWADDRESS); + } + if enabled.contains(SanitizerSet::SAFESTACK) && is_ignored(c"safestack") { + enabled.remove(SanitizerSet::SAFESTACK); + } + if is_ignored(c"cfi") { + to_add.push(llvm::CreateAttrString(cx.llcx, "no-sanitize-cfi")); + } + if is_ignored(c"kcfi") { + to_add.push(llvm::CreateAttrString(cx.llcx, "no-sanitize-kcfi")); + } + } + } + to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers, enabled)); // For non-naked functions, set branch protection attributes on aarch64. if let Some(BranchProtection { bti, pac_ret, gcs }) = diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index a7dec13422f46..00fda6a2d54c8 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -123,7 +123,12 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + let attrs = attributes::sanitize_attrs( + &cx, + tcx, + SanitizerFnAttrs::default(), + tcx.sess.sanitizers(), + ); attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 134bc5006dd00..9ae051547e43c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1832,7 +1832,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // Emit KCFI operand bundle let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); - if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) { + if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|bundle| bundle.as_ref()) { bundles.push(kcfi_bundle); } @@ -1875,6 +1875,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { { return; } + if crate::llvm::HasStringAttribute(self.llfn(), "no-sanitize-cfi") { + return; + } let mut options = cfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { @@ -1884,6 +1887,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } + if self.cx.is_sanitizer_type_ignored(c"cfi", fn_abi) { + return; + } + let typeid = if let Some(instance) = instance { cfi::typeid_for_instance(self.tcx, instance, options) } else { @@ -1933,6 +1940,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { { return None; } + if crate::llvm::HasStringAttribute(self.llfn(), "no-sanitize-kcfi") { + return None; + } let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { @@ -1942,6 +1952,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } + if self.cx.is_sanitizer_type_ignored(c"kcfi", fn_abi) { + return None; + } + let kcfi_typeid = if let Some(instance) = instance { kcfi::typeid_for_instance(self.tcx, instance, options) } else { diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 5c5e9ed7e082c..9bca4d6cef55a 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -13,7 +13,7 @@ use rustc_hashes::Hash128; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::cstore::DllImport; use tracing::debug; @@ -446,3 +446,17 @@ impl AsCCharPtr for [u8] { self.as_ptr().cast() } } + +pub(crate) fn type_name_for_ignore_list<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, +) -> String { + let inputs: Vec<_> = fn_abi.args.iter().map(|arg| arg.layout.ty).collect(); + let output = fn_abi.ret.layout.ty; + let mut fn_sig_kind = ty::FnSigKind::default(); + fn_sig_kind = fn_sig_kind.set_safety(rustc_hir::Safety::Safe); + fn_sig_kind = fn_sig_kind.set_c_variadic(fn_abi.c_variadic); + let fn_sig = tcx.mk_fn_sig(inputs, output, fn_sig_kind); + let fn_ptr = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig)); + ty::print::with_no_trimmed_paths!(fn_ptr.to_string()) +} diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index ab3e4b8d9effa..973a25edc9e2f 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -509,6 +509,52 @@ impl<'ll> CodegenCx<'ll, '_> { base::set_variable_sanitizer_attrs(g, attrs); + if let Some(ignorelist) = &self.sanitizer_ignorelist { + let instance = ty::Instance::mono(self.tcx, def_id); + let sym_name = self.tcx.symbol_name(instance).name; + let span = self.tcx.def_span(def_id); + let source_map = self.tcx.sess.source_map(); + let filename = + source_map.span_to_filename(span).prefer_local_unconditionally().to_string(); + let ty_name = rustc_middle::ty::print::with_no_trimmed_paths!( + self.tcx.type_of(def_id).skip_binder().to_string() + ); + let mainfile = self + .tcx + .sess + .local_crate_source_file() + .and_then(|path| path.local_path().map(|p| p.display().to_string())) + .unwrap_or_default(); + + let is_ignored = |section: &std::ffi::CStr| -> bool { + ignorelist.contains_prefix(section, c"global", sym_name) + || ignorelist.contains_prefix(section, c"src", &filename) + || (!mainfile.is_empty() + && ignorelist.contains_prefix(section, c"mainfile", &mainfile)) + || ignorelist.contains_prefix(section, c"type", &ty_name) + }; + + let sanitizers = self.tcx.sess.sanitizers(); + let ignore_address = is_ignored(c"address"); + let ignore_kernel_address = ignore_address || is_ignored(c"kernel-address"); + let ignore_hwaddress = is_ignored(c"hwaddress"); + let ignore_kernel_hwaddress = ignore_hwaddress || is_ignored(c"kernel-hwaddress"); + + if (sanitizers.contains(rustc_target::spec::SanitizerSet::ADDRESS) && ignore_address) + || (sanitizers.contains(rustc_target::spec::SanitizerSet::KERNELADDRESS) + && ignore_kernel_address) + { + unsafe { llvm::LLVMRustSetNoSanitizeAddress(g) }; + } + if (sanitizers.contains(rustc_target::spec::SanitizerSet::HWADDRESS) + && ignore_hwaddress) + || (sanitizers.contains(rustc_target::spec::SanitizerSet::KERNELHWADDRESS) + && ignore_kernel_hwaddress) + { + unsafe { llvm::LLVMRustSetNoSanitizeHWAddress(g) }; + } + } + if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) { // `USED` and `USED_LINKER` can't be used together. assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 85a7f9cab73a4..c824c43e28ef3 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -133,6 +133,7 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// Extra per-CGU codegen state needed when coverage instrumentation is enabled. pub coverage_cx: Option>, pub dbg_cx: Option>, + pub sanitizer_ignorelist: Option, eh_personality: Cell>, pub rust_try_fn: Cell>, @@ -650,6 +651,26 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { None }; + // FIXME: This parses the ignorelist files for each CGU, which adds a performance overhead. + // Clang parses it once per frontend invocation. LLVM's `SpecialCaseList::inSection` + // mutates an internal `LazyInit` cache and is not thread-safe. We either need to wrap + // the queries in a lock or wait for LLVM to expose a thread-safe way to query it. + let sanitizer_ignorelist = if !tcx.sess.opts.unstable_opts.sanitizer_ignorelist.is_empty() { + for path in &tcx.sess.opts.unstable_opts.sanitizer_ignorelist { + let _ = tcx.sess.source_map().load_file(std::path::Path::new(path)); + } + match crate::llvm::SanitizerIgnoreList::new( + &tcx.sess.opts.unstable_opts.sanitizer_ignorelist, + ) { + Ok(list) => Some(list), + Err(err) => { + tcx.dcx().fatal(format!("failed to parse sanitizer ignorelist: {}", err)); + } + } + } else { + None + }; + GenericCx( FullCx { tcx, @@ -669,6 +690,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { scalar_lltypes: Default::default(), coverage_cx, dbg_cx, + sanitizer_ignorelist, eh_personality: Cell::new(None), rust_try_fn: Cell::new(None), intrinsics: Default::default(), @@ -766,6 +788,17 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { 1 << 6, ); } + + pub(crate) fn is_sanitizer_type_ignored( + &self, + sanitizer: &std::ffi::CStr, + fn_abi: &rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, + ) -> bool { + self.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { + let type_name = crate::common::type_name_for_ignore_list(self.tcx, fn_abi); + ignorelist.contains_prefix(sanitizer, c"type", &type_name) + }) + } } impl<'ll> SimpleCx<'ll> { pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 419d38f95e595..63276d1d3bec3 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -190,39 +190,47 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { ); fn_abi.apply_attrs_llfn(self, llfn, instance); - if self.tcx.sess.is_sanitizer_cfi_enabled() { - if let Some(instance) = instance { - let mut typeids = FxIndexSet::default(); - for options in [ - cfi::TypeIdOptions::GENERALIZE_POINTERS, - cfi::TypeIdOptions::NORMALIZE_INTEGERS, - cfi::TypeIdOptions::USE_CONCRETE_SELF, - ] - .into_iter() - .powerset() - .map(cfi::TypeIdOptions::from_iter) - { - let typeid = cfi::typeid_for_instance(self.tcx, instance, options); - if typeids.insert(typeid.clone()) { + if self.tcx.sess.is_sanitizer_cfi_enabled() + && !crate::llvm::HasStringAttribute(llfn, "no-sanitize-cfi") + { + let ignored = self.is_sanitizer_type_ignored(c"cfi", fn_abi); + + if !ignored { + if let Some(instance) = instance { + let mut typeids = FxIndexSet::default(); + for options in [ + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + cfi::TypeIdOptions::USE_CONCRETE_SELF, + ] + .into_iter() + .powerset() + .map(cfi::TypeIdOptions::from_iter) + { + let typeid = cfi::typeid_for_instance(self.tcx, instance, options); + if typeids.insert(typeid.clone()) { + self.add_type_metadata(llfn, typeid.as_bytes()); + } + } + } else { + for options in [ + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + ] + .into_iter() + .powerset() + .map(cfi::TypeIdOptions::from_iter) + { + let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options); self.add_type_metadata(llfn, typeid.as_bytes()); } } - } else { - for options in [ - cfi::TypeIdOptions::GENERALIZE_POINTERS, - cfi::TypeIdOptions::NORMALIZE_INTEGERS, - ] - .into_iter() - .powerset() - .map(cfi::TypeIdOptions::from_iter) - { - let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options); - self.add_type_metadata(llfn, typeid.as_bytes()); - } } } - if self.tcx.sess.is_sanitizer_kcfi_enabled() { + if self.tcx.sess.is_sanitizer_kcfi_enabled() + && !crate::llvm::HasStringAttribute(llfn, "no-sanitize-kcfi") + { // LLVM KCFI does not support multiple !kcfi_type attachments let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { @@ -232,11 +240,14 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } - if let Some(instance) = instance { - let kcfi_typeid = kcfi::typeid_for_instance(self.tcx, instance, options); - self.set_kcfi_type_metadata(llfn, kcfi_typeid); - } else { - let kcfi_typeid = kcfi::typeid_for_fnabi(self.tcx, fn_abi, options); + let ignored = self.is_sanitizer_type_ignored(c"kcfi", fn_abi); + + if !ignored { + let kcfi_typeid = if let Some(instance) = instance { + kcfi::typeid_for_instance(self.tcx, instance, options) + } else { + kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) + }; self.set_kcfi_type_metadata(llfn, kcfi_typeid); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 919868b9005eb..4d388bcead590 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -690,7 +690,7 @@ pub(crate) enum CompressionKind { } unsafe extern "C" { - type Opaque; + pub(crate) type Opaque; } #[repr(C)] struct InvariantOpaque<'a> { @@ -2500,6 +2500,19 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); + pub(crate) fn LLVMRustSpecialCaseListCreate( + Paths: *const *const c_char, + NumPaths: size_t, + ErrorMsg: &RustString, + ) -> *mut Opaque; + pub(crate) fn LLVMRustSpecialCaseListDestroy(List: *mut Opaque); + pub(crate) fn LLVMRustSpecialCaseListContainsPrefix( + List: *const Opaque, + Section: *const c_char, + Prefix: *const c_char, + Query: *const c_char, + ) -> bool; + pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString); pub(crate) fn LLVMRustUnpackOptimizationDiagnostic<'a>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2ec19b1795b5a..aac36e6433fa8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -358,6 +358,52 @@ pub(crate) fn build_byte_buffer(f: impl FnOnce(&RustString)) -> Vec { RustString::build_byte_buffer(f) } +pub(crate) struct SanitizerIgnoreList { + inner: *mut ffi::Opaque, +} + +impl SanitizerIgnoreList { + pub(crate) fn new(paths: &[String]) -> Result { + use std::ffi::CString; + let c_paths: Vec = + paths.iter().map(|p| CString::new(p.as_str()).unwrap()).collect(); + let c_ptrs: Vec<*const libc::c_char> = c_paths.iter().map(|c| c.as_ptr()).collect(); + + let mut inner = std::ptr::null_mut(); + let err = build_string(|err| unsafe { + inner = ffi::LLVMRustSpecialCaseListCreate(c_ptrs.as_ptr(), c_ptrs.len(), err); + }); + + let err = err.unwrap_or_else(|e| format!("utf8 error: {}", e)); + if inner.is_null() { Err(err) } else { Ok(Self { inner }) } + } + + pub(crate) fn contains_prefix( + &self, + section: &std::ffi::CStr, + prefix: &std::ffi::CStr, + query: &str, + ) -> bool { + let query = std::ffi::CString::new(query).unwrap(); + unsafe { + ffi::LLVMRustSpecialCaseListContainsPrefix( + self.inner, + section.as_ptr(), + prefix.as_ptr(), + query.as_ptr(), + ) + } + } +} + +impl Drop for SanitizerIgnoreList { + fn drop(&mut self) { + unsafe { + ffi::LLVMRustSpecialCaseListDestroy(self.inner); + } + } +} + pub(crate) fn twine_to_string(tr: &Twine) -> String { unsafe { build_string(|s| LLVMRustWriteTwineToString(tr, s)).expect("got a non-UTF8 Twine from LLVM") diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 0cbda23f384cc..ba8d00648425d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -21,6 +21,7 @@ enum class LLVMRustResult { Success, Failure }; typedef struct OpaqueRustString *RustStringRef; typedef struct LLVMOpaqueTwine *LLVMTwineRef; typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef; +typedef struct LLVMOpaqueSpecialCaseList *LLVMSpecialCaseListRef; extern "C" void LLVMRustStringWriteImpl(RustStringRef buf, const char *slice_ptr, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a50b06e5120b2..a5b9ccd528219 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -34,8 +34,10 @@ #include "llvm/Support/JSON.h" #include "llvm/Support/ModRef.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/Timer.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include @@ -1895,3 +1897,35 @@ FIXED_MD_KIND(MD_noalias_addrspace, 41) // LLVM versions, it's fine to omit them from this list; in that case Rust-side // code cannot declare them as fixed IDs and must look them up by name instead. #undef FIXED_MD_KIND + +extern "C" LLVMSpecialCaseListRef +LLVMRustSpecialCaseListCreate(const char **Paths, size_t NumPaths, + RustStringRef ErrorMsg) { + std::string Error; + std::vector PathsVec(Paths, Paths + NumPaths); + std::unique_ptr SCL = llvm::SpecialCaseList::create( + PathsVec, *llvm::vfs::getRealFileSystem(), Error); + if (!SCL) { + LLVMRustStringWriteImpl(ErrorMsg, Error.data(), Error.size()); + return nullptr; + } + return reinterpret_cast(SCL.release()); +} + +extern "C" void LLVMRustSpecialCaseListDestroy(LLVMSpecialCaseListRef List) { + delete reinterpret_cast(List); +} + +extern "C" bool +LLVMRustSpecialCaseListContainsPrefix(LLVMSpecialCaseListRef List, + const char *Section, const char *Prefix, + const char *Query) { + auto *SCL = reinterpret_cast(List); + std::pair NoSan = + SCL->inSectionBlame(Section, Prefix, Query); + if (NoSan.second == 0) + return false; + std::pair San = + SCL->inSectionBlame(Section, Prefix, Query, "sanitize"); + return San.second == 0 || NoSan > San; +} diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6ae0d9721bc00..de4365e13f119 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2620,6 +2620,8 @@ written to standard error output)"), #[rustc_lint_opt_deny_field_access("use `Session::sanitizers()` instead of this field")] sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED] { TARGET_MODIFIER: Sanitizer }, "use a sanitizer"), + sanitizer_ignorelist: Vec = (vec![], parse_list, [TRACKED], + "list of files providing ignorelists for sanitizers"), sanitizer_cfi_canonical_jump_tables: Option = (Some(true), parse_opt_bool, [TRACKED], "enable canonical jump tables (default: yes)"), sanitizer_cfi_generalize_pointers: Option = (None, parse_opt_bool, [TRACKED], diff --git a/tests/codegen-llvm/sanitizer/cfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/cfi-ignorelist.rs new file mode 100644 index 0000000000000..8871e7f0dcd39 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/cfi-ignorelist.rs @@ -0,0 +1,22 @@ +//@ needs-sanitizer-cfi +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_cfi +// CHECK-NOT: !type +#[no_mangle] +pub fn test_cfi(f: fn(), x: &mut i32) { + *x = 1; + // CHECK-NOT: trap + f(); +} + +// CHECK: define void @test_memory +// CHECK-SAME: !type +#[no_mangle] +pub fn test_memory(f: fn(i32), x: &mut i32) { + *x = 2; + // CHECK: trap + f(1); +} diff --git a/tests/codegen-llvm/sanitizer/global-ignorelist.rs b/tests/codegen-llvm/sanitizer/global-ignorelist.rs new file mode 100644 index 0000000000000..994dccd834f1c --- /dev/null +++ b/tests/codegen-llvm/sanitizer/global-ignorelist.rs @@ -0,0 +1,13 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/global-ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: @IGNORED_GLOBAL = {{.*}} no_sanitize_address +#[no_mangle] +pub static IGNORED_GLOBAL: i64 = 42; + +// CHECK: @CHECKED_GLOBAL = {{.*}} no_sanitize_address +// (because of src:*global-ignorelist.rs) +#[no_mangle] +pub static CHECKED_GLOBAL: i64 = 42; diff --git a/tests/codegen-llvm/sanitizer/global-ignorelist.txt b/tests/codegen-llvm/sanitizer/global-ignorelist.txt new file mode 100644 index 0000000000000..b82472fb39422 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/global-ignorelist.txt @@ -0,0 +1,3 @@ +[address] +global:*IGNORED_GLOBAL* +src:*global-ignorelist.rs diff --git a/tests/codegen-llvm/sanitizer/ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist.rs new file mode 100644 index 0000000000000..d71a17b4b3c1b --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist.rs @@ -0,0 +1,74 @@ +//@ revisions: ASAN MSAN TSAN HWASAN SAFESTACK +//@[ASAN] needs-sanitizer-address +//@[MSAN] needs-sanitizer-memory +//@[TSAN] needs-sanitizer-thread +//@[HWASAN] needs-sanitizer-hwaddress +//@[SAFESTACK] needs-sanitizer-safestack +//@ compile-flags: -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt -Cunsafe-allow-abi-mismatch=sanitizer +//@ [ASAN] compile-flags: -Zsanitizer=address +//@ [MSAN] compile-flags: -Zsanitizer=memory +//@ [TSAN] compile-flags: -Zsanitizer=thread +//@ [HWASAN] compile-flags: -Zsanitizer=hwaddress -C target-feature=+tagged-globals +//@ [SAFESTACK] compile-flags: -Zsanitizer=safestack + +#![crate_type = "lib"] + +// CHECK: ; Function Attrs: +// ASAN-NOT: sanitize_address +// MSAN-SAME: sanitize_memory +// TSAN-SAME: sanitize_thread +// HWASAN-SAME: sanitize_hwaddress +// SAFESTACK-SAME: safestack +// CHECK-NEXT: define void @test_address +#[no_mangle] +pub fn test_address(x: &mut i32) { + *x = 1; +} + +// CHECK: ; Function Attrs: +// ASAN-SAME: sanitize_address +// MSAN-NOT: sanitize_memory +// TSAN-SAME: sanitize_thread +// HWASAN-SAME: sanitize_hwaddress +// SAFESTACK-SAME: safestack +// CHECK-NEXT: define void @test_memory +#[no_mangle] +pub fn test_memory(x: &mut i32) { + *x = 2; +} + +// CHECK: ; Function Attrs: +// ASAN-SAME: sanitize_address +// MSAN-SAME: sanitize_memory +// TSAN-NOT: sanitize_thread +// HWASAN-SAME: sanitize_hwaddress +// SAFESTACK-SAME: safestack +// CHECK-NEXT: define void @test_thread +#[no_mangle] +pub fn test_thread(x: &mut i32) { + *x = 3; +} + +// CHECK: ; Function Attrs: +// ASAN-SAME: sanitize_address +// MSAN-SAME: sanitize_memory +// TSAN-SAME: sanitize_thread +// HWASAN-NOT: sanitize_hwaddress +// SAFESTACK-SAME: safestack +// CHECK-NEXT: define void @test_hwaddress +#[no_mangle] +pub fn test_hwaddress(x: &mut i32) { + *x = 4; +} + +// CHECK: ; Function Attrs: +// ASAN-SAME: sanitize_address +// MSAN-SAME: sanitize_memory +// TSAN-SAME: sanitize_thread +// HWASAN-SAME: sanitize_hwaddress +// SAFESTACK-NOT: safestack +// CHECK-NEXT: define void @test_safestack +#[no_mangle] +pub fn test_safestack(x: &mut i32) { + *x = 5; +} diff --git a/tests/codegen-llvm/sanitizer/ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist.txt new file mode 100644 index 0000000000000..16c9b8b284714 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist.txt @@ -0,0 +1,38 @@ +[address] +fun:*test_address* +src:*src-ignore-memory.rs + +[memory] +fun:*test_memory* +src:*src-ignore-memory.rs + +[thread] +fun:*test_thread* +src:*src-ignore-memory.rs + +[hwaddress] +fun:*test_hwaddress* +src:*src-ignore-memory.rs + +[safestack] +fun:*test_safestack* +src:*src-ignore-memory.rs + +[cfi] +fun:*test_cfi* +src:*src-ignore* +type:fn() + +[kcfi] +fun:*test_kcfi* +src:*src-ignore* +type:fn() + +[address] +type:i32 + +[hwaddress] +type:i32 + +[address] +type:MyStruct diff --git a/tests/codegen-llvm/sanitizer/kcfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/kcfi-ignorelist.rs new file mode 100644 index 0000000000000..207a561714b78 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/kcfi-ignorelist.rs @@ -0,0 +1,18 @@ +//@ needs-sanitizer-kcfi +//@ compile-flags: -Zsanitizer=kcfi -C panic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_kcfi +// CHECK-NOT: !kcfi_type +#[no_mangle] +pub fn test_kcfi(x: &mut i32) { + *x = 1; +} + +// CHECK: define void @test_memory +// CHECK-SAME: !kcfi_type +#[no_mangle] +pub fn test_memory(x: &mut i32) { + *x = 2; +} diff --git a/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.rs b/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.rs new file mode 100644 index 0000000000000..c6a59365f1671 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.rs @@ -0,0 +1,20 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/kernel-address-ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: ; Function Attrs: +// CHECK-SAME: sanitize_address +// CHECK-NEXT: define void @test_kernel_address_ignored +#[no_mangle] +pub fn test_kernel_address_ignored(x: &mut i32) { + *x = 1; +} + +// CHECK: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK-NEXT: define void @test_address_ignored +#[no_mangle] +pub fn test_address_ignored(x: &mut i32) { + *x = 2; +} diff --git a/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.txt b/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.txt new file mode 100644 index 0000000000000..c21e081a1057f --- /dev/null +++ b/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.txt @@ -0,0 +1,5 @@ +[kernel-address] +fun:test_kernel_address_ignored + +[address] +fun:test_address_ignored diff --git a/tests/codegen-llvm/sanitizer/mainfile-ignore.rs b/tests/codegen-llvm/sanitizer/mainfile-ignore.rs new file mode 100644 index 0000000000000..683f739c53be2 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/mainfile-ignore.rs @@ -0,0 +1,12 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/mainfile-ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK-NEXT: define void @test_mainfile +#[no_mangle] +pub fn test_mainfile(x: &mut i32) { + *x = 1; +} diff --git a/tests/codegen-llvm/sanitizer/mainfile-ignorelist.txt b/tests/codegen-llvm/sanitizer/mainfile-ignorelist.txt new file mode 100644 index 0000000000000..e446cec256466 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/mainfile-ignorelist.txt @@ -0,0 +1,2 @@ +[address] +mainfile:*mainfile-ignore.rs diff --git a/tests/codegen-llvm/sanitizer/override-ignorelist.rs b/tests/codegen-llvm/sanitizer/override-ignorelist.rs new file mode 100644 index 0000000000000..74743cfc27358 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/override-ignorelist.rs @@ -0,0 +1,20 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/override-ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK-NEXT: define void @test_ignored +#[no_mangle] +pub fn test_ignored(x: &mut i32) { + *x = 1; +} + +// CHECK: ; Function Attrs: +// CHECK-SAME: sanitize_address +// CHECK-NEXT: define void @test_re_enabled +#[no_mangle] +pub fn test_re_enabled(x: &mut i32) { + *x = 2; +} diff --git a/tests/codegen-llvm/sanitizer/override-ignorelist.txt b/tests/codegen-llvm/sanitizer/override-ignorelist.txt new file mode 100644 index 0000000000000..77d66c8341bce --- /dev/null +++ b/tests/codegen-llvm/sanitizer/override-ignorelist.txt @@ -0,0 +1,3 @@ +[address] +fun:* +fun:test_re_enabled=sanitize diff --git a/tests/codegen-llvm/sanitizer/src-ignore-memory.rs b/tests/codegen-llvm/sanitizer/src-ignore-memory.rs new file mode 100644 index 0000000000000..9799e9b522f03 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/src-ignore-memory.rs @@ -0,0 +1,74 @@ +//@ revisions: ASAN MSAN TSAN HWASAN SAFESTACK +//@[ASAN] needs-sanitizer-address +//@[MSAN] needs-sanitizer-memory +//@[TSAN] needs-sanitizer-thread +//@[HWASAN] needs-sanitizer-hwaddress +//@[SAFESTACK] needs-sanitizer-safestack +//@ compile-flags: -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt -Cunsafe-allow-abi-mismatch=sanitizer +//@ [ASAN] compile-flags: -Zsanitizer=address +//@ [MSAN] compile-flags: -Zsanitizer=memory +//@ [TSAN] compile-flags: -Zsanitizer=thread +//@ [HWASAN] compile-flags: -Zsanitizer=hwaddress -C target-feature=+tagged-globals +//@ [SAFESTACK] compile-flags: -Zsanitizer=safestack + +#![crate_type = "lib"] + +// CHECK: ; Function Attrs: +// ASAN-NOT: sanitize_address +// MSAN-NOT: sanitize_memory +// TSAN-NOT: sanitize_thread +// HWASAN-NOT: sanitize_hwaddress +// SAFESTACK-NOT: safestack +// CHECK-NEXT: define void @test_file_address +#[no_mangle] +pub fn test_file_address(x: &mut i32) { + *x = 1; +} + +// CHECK: ; Function Attrs: +// ASAN-NOT: sanitize_address +// MSAN-NOT: sanitize_memory +// TSAN-NOT: sanitize_thread +// HWASAN-NOT: sanitize_hwaddress +// SAFESTACK-NOT: safestack +// CHECK-NEXT: define void @test_file_memory +#[no_mangle] +pub fn test_file_memory(x: &mut i32) { + *x = 2; +} + +// CHECK: ; Function Attrs: +// ASAN-NOT: sanitize_address +// MSAN-NOT: sanitize_memory +// TSAN-NOT: sanitize_thread +// HWASAN-NOT: sanitize_hwaddress +// SAFESTACK-NOT: safestack +// CHECK-NEXT: define void @test_file_thread +#[no_mangle] +pub fn test_file_thread(x: &mut i32) { + *x = 3; +} + +// CHECK: ; Function Attrs: +// ASAN-NOT: sanitize_address +// MSAN-NOT: sanitize_memory +// TSAN-NOT: sanitize_thread +// HWASAN-NOT: sanitize_hwaddress +// SAFESTACK-NOT: safestack +// CHECK-NEXT: define void @test_file_hwaddress +#[no_mangle] +pub fn test_file_hwaddress(x: &mut i32) { + *x = 4; +} + +// CHECK: ; Function Attrs: +// ASAN-NOT: sanitize_address +// MSAN-NOT: sanitize_memory +// TSAN-NOT: sanitize_thread +// HWASAN-NOT: sanitize_hwaddress +// SAFESTACK-NOT: safestack +// CHECK-NEXT: define void @test_file_safestack +#[no_mangle] +pub fn test_file_safestack(x: &mut i32) { + *x = 5; +} diff --git a/tests/codegen-llvm/sanitizer/src-ignore.rs b/tests/codegen-llvm/sanitizer/src-ignore.rs new file mode 100644 index 0000000000000..dfacb473c8ab8 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/src-ignore.rs @@ -0,0 +1,13 @@ +//@ needs-sanitizer-cfi +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_file +// CHECK-NOT: !type +#[no_mangle] +pub fn test_file(f: fn(), x: &mut i32) { + *x = 1; + // CHECK-NOT: trap + f(); +} diff --git a/tests/codegen-llvm/sanitizer/type-ignorelist-asan.rs b/tests/codegen-llvm/sanitizer/type-ignorelist-asan.rs new file mode 100644 index 0000000000000..6f04e863fb522 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/type-ignorelist-asan.rs @@ -0,0 +1,21 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: @IGNORED_GLOBAL = {{.*}} no_sanitize_address +#[no_mangle] +pub static IGNORED_GLOBAL: i32 = 42; + +// CHECK: @CHECKED_GLOBAL = +// CHECK-NOT: no_sanitize_address +#[no_mangle] +pub static CHECKED_GLOBAL: i64 = 42; + +pub struct MyStruct { + x: i32, +} + +// CHECK: @MY_STRUCT = {{.*}} no_sanitize_address +#[no_mangle] +pub static MY_STRUCT: MyStruct = MyStruct { x: 42 }; diff --git a/tests/codegen-llvm/sanitizer/type-ignorelist-kcfi.rs b/tests/codegen-llvm/sanitizer/type-ignorelist-kcfi.rs new file mode 100644 index 0000000000000..8010ce19be25a --- /dev/null +++ b/tests/codegen-llvm/sanitizer/type-ignorelist-kcfi.rs @@ -0,0 +1,18 @@ +//@ needs-sanitizer-kcfi +//@ compile-flags: -Zsanitizer=kcfi -Cpanic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_type +// CHECK-SAME: !kcfi_type +#[no_mangle] +pub fn test_type(f: fn(), x: &mut i32) { + *x = 1; + // CHECK-NOT: !kcfi_type + f(); +} + +// CHECK: define void @test_type_2() +// CHECK-NOT: !kcfi_type +#[no_mangle] +pub fn test_type_2() {} diff --git a/tests/codegen-llvm/sanitizer/type-ignorelist.rs b/tests/codegen-llvm/sanitizer/type-ignorelist.rs new file mode 100644 index 0000000000000..6ddbb709b2d98 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/type-ignorelist.rs @@ -0,0 +1,20 @@ +//@ needs-sanitizer-cfi +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_type +// CHECK-SAME: !type +#[no_mangle] +pub fn test_type(f: fn(), x: &mut i32) { + *x = 1; + // CHECK-NOT: trap + f(); +} + +// Ensure the function definition of test_type_2 has no !type metadata +// since it has the type `fn()` which is ignored +// CHECK: define void @test_type_2() +// CHECK-NOT: !type +#[no_mangle] +pub fn test_type_2() {} diff --git a/tests/codegen-llvm/sanitizer/type-string-unsafe-ignorelist.txt b/tests/codegen-llvm/sanitizer/type-string-unsafe-ignorelist.txt new file mode 100644 index 0000000000000..df3db6b38ceb0 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/type-string-unsafe-ignorelist.txt @@ -0,0 +1,2 @@ +[address] +type:*unsafe*extern*fn* diff --git a/tests/codegen-llvm/sanitizer/type-string-unsafe.rs b/tests/codegen-llvm/sanitizer/type-string-unsafe.rs new file mode 100644 index 0000000000000..f4e7b83bf61b0 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/type-string-unsafe.rs @@ -0,0 +1,10 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/type-string-unsafe-ignorelist.txt + +#![crate_type = "lib"] + +pub static MY_FN: unsafe extern "C" fn() = my_fn_impl; + +// CHECK: MY_FN = {{.*}} no_sanitize_address + +unsafe extern "C" fn my_fn_impl() {}