From 2f733b5ba97e401e62d54f44c3ee43ccc7e01bb9 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Wed, 10 Jun 2026 15:49:36 +0000 Subject: [PATCH 1/9] sanitizers: Implement support for the sanitize ignorelist The sanitize ignorelist gives central controls over which functions, files, etc. should be ignored and not sanitized. It is a common file format for clang and explained here: https://clang.llvm.org/docs/SanitizerSpecialCaseList.html. This change adds support for this list in Rust as well. --- compiler/rustc_codegen_llvm/src/abi.rs | 1 + compiler/rustc_codegen_llvm/src/allocator.rs | 2 +- compiler/rustc_codegen_llvm/src/attributes.rs | 65 +++++++++++++- compiler/rustc_codegen_llvm/src/base.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 88 ++++++++++++++----- compiler/rustc_codegen_llvm/src/consts.rs | 46 ++++++++++ compiler/rustc_codegen_llvm/src/context.rs | 22 +++++ compiler/rustc_codegen_llvm/src/declare.rs | 55 ++++++++++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 +++- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 46 ++++++++++ .../rustc_llvm/llvm-wrapper/LLVMWrapper.h | 1 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 34 +++++++ compiler/rustc_session/src/options.rs | 2 + .../codegen-llvm/sanitizer/cfi-ignorelist.rs | 22 +++++ .../sanitizer/global-ignorelist.rs | 13 +++ .../sanitizer/global-ignorelist.txt | 3 + tests/codegen-llvm/sanitizer/ignorelist.rs | 74 ++++++++++++++++ tests/codegen-llvm/sanitizer/ignorelist.txt | 38 ++++++++ .../codegen-llvm/sanitizer/kcfi-ignorelist.rs | 18 ++++ .../sanitizer/kernel-address-ignorelist.rs | 20 +++++ .../sanitizer/kernel-address-ignorelist.txt | 5 ++ .../codegen-llvm/sanitizer/mainfile-ignore.rs | 12 +++ .../sanitizer/mainfile-ignorelist.txt | 2 + .../sanitizer/override-ignorelist.rs | 20 +++++ .../sanitizer/override-ignorelist.txt | 3 + .../sanitizer/src-ignore-memory.rs | 74 ++++++++++++++++ tests/codegen-llvm/sanitizer/src-ignore.rs | 13 +++ .../sanitizer/type-ignorelist-asan.rs | 21 +++++ .../sanitizer/type-ignorelist-kcfi.rs | 18 ++++ .../codegen-llvm/sanitizer/type-ignorelist.rs | 20 +++++ .../type-string-unsafe-ignorelist.txt | 2 + .../sanitizer/type-string-unsafe.rs | 10 +++ 32 files changed, 729 insertions(+), 38 deletions(-) create mode 100644 tests/codegen-llvm/sanitizer/cfi-ignorelist.rs create mode 100644 tests/codegen-llvm/sanitizer/global-ignorelist.rs create mode 100644 tests/codegen-llvm/sanitizer/global-ignorelist.txt create mode 100644 tests/codegen-llvm/sanitizer/ignorelist.rs create mode 100644 tests/codegen-llvm/sanitizer/ignorelist.txt create mode 100644 tests/codegen-llvm/sanitizer/kcfi-ignorelist.rs create mode 100644 tests/codegen-llvm/sanitizer/kernel-address-ignorelist.rs create mode 100644 tests/codegen-llvm/sanitizer/kernel-address-ignorelist.txt create mode 100644 tests/codegen-llvm/sanitizer/mainfile-ignore.rs create mode 100644 tests/codegen-llvm/sanitizer/mainfile-ignorelist.txt create mode 100644 tests/codegen-llvm/sanitizer/override-ignorelist.rs create mode 100644 tests/codegen-llvm/sanitizer/override-ignorelist.txt create mode 100644 tests/codegen-llvm/sanitizer/src-ignore-memory.rs create mode 100644 tests/codegen-llvm/sanitizer/src-ignore.rs create mode 100644 tests/codegen-llvm/sanitizer/type-ignorelist-asan.rs create mode 100644 tests/codegen-llvm/sanitizer/type-ignorelist-kcfi.rs create mode 100644 tests/codegen-llvm/sanitizer/type-ignorelist.rs create mode 100644 tests/codegen-llvm/sanitizer/type-string-unsafe-ignorelist.txt create mode 100644 tests/codegen-llvm/sanitizer/type-string-unsafe.rs diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 816ebe3fcf3d9..2f2b6ba8ae97b 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -599,6 +599,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 b20df0a6bad02..5eec4be87a3bc 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 9415c2ecb9d10..8f20cd966f849 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -137,9 +137,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)); } @@ -476,6 +476,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(); @@ -537,7 +538,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 }) = sess.branch_protection() { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 14700266412dd..30c5de39c25d5 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -129,7 +129,7 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default(), tcx.sess.sanitizers()); // When pointer authentication is enabled, ensure that the ptrauth-* attributes are // also attached to the entry wrapper. // diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 87c941cdeb23a..7ba5de7ea7e5a 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1961,7 +1961,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); } @@ -2009,6 +2009,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() { @@ -2018,11 +2021,29 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } + 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 = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); + let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); + let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + let typeid = if let Some(instance) = instance { cfi::typeid_for_instance(self.tcx, instance, options) } else { cfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; + + if self + .cx + .sanitizer_ignorelist + .as_ref() + .is_some_and(|ignorelist| ignorelist.contains_prefix(c"cfi", c"type", &type_name)) + { + return; + } let typeid_metadata = self.cx.create_metadata(typeid.as_bytes()); let dbg_loc = self.get_dbg_loc(); @@ -2114,34 +2135,53 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llfn: &'ll Value, ) -> Option> { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; - let kcfi_bundle = if self.tcx.sess.is_sanitizer_kcfi_enabled() - && let Some(fn_abi) = fn_abi - && is_indirect_call - { - if let Some(fn_attrs) = fn_attrs - && fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI) + let kcfi_bundle = + if self.tcx.sess.is_sanitizer_kcfi_enabled() + && let Some(fn_abi) = fn_abi + && is_indirect_call { - return None; - } + if let Some(fn_attrs) = fn_attrs + && fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI) + { + 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() { - options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); - } - if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); - } + let mut options = kcfi::TypeIdOptions::empty(); + if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); + } + if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); + } + + 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 = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); + let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); + let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + + if self.cx.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { + ignorelist.contains_prefix(c"kcfi", c"type", &type_name) + }) { + return None; + } + + 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) + }; - let kcfi_typeid = if let Some(instance) = instance { - kcfi::typeid_for_instance(self.tcx, instance, options) + Some(llvm::OperandBundleBox::new("kcfi", &[self.const_u32(kcfi_typeid)])) } else { - kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) + None }; - - Some(llvm::OperandBundleBox::new("kcfi", &[self.const_u32(kcfi_typeid)])) - } else { - None - }; kcfi_bundle } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 24e52743fbe30..b9e5205381eef 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -570,6 +570,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 853c4bfc9ca3f..f2c759050e863 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>, @@ -680,6 +681,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, @@ -699,6 +720,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(), diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 419d38f95e595..2636da108315b 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -190,7 +190,18 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { ); fn_abi.apply_attrs_llfn(self, llfn, instance); - if self.tcx.sess.is_sanitizer_cfi_enabled() { + if self.tcx.sess.is_sanitizer_cfi_enabled() + && !crate::llvm::HasStringAttribute(llfn, "no-sanitize-cfi") + { + 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 = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); + let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); + let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + if let Some(instance) = instance { let mut typeids = FxIndexSet::default(); for options in [ @@ -203,7 +214,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { .map(cfi::TypeIdOptions::from_iter) { let typeid = cfi::typeid_for_instance(self.tcx, instance, options); - if typeids.insert(typeid.clone()) { + let ignored = self.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { + ignorelist.contains_prefix(c"cfi", c"type", &type_name) + }); + if !ignored && typeids.insert(typeid.clone()) { self.add_type_metadata(llfn, typeid.as_bytes()); } } @@ -217,12 +231,20 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { .map(cfi::TypeIdOptions::from_iter) { let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options); - self.add_type_metadata(llfn, typeid.as_bytes()); + let ignored = self + .sanitizer_ignorelist + .as_ref() + .is_some_and(|l| l.contains_prefix(c"cfi", c"type", &type_name)); + if !ignored { + 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 +254,26 @@ 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 inputs: Vec<_> = fn_abi.args.iter().map(|arg| arg.layout.ty).collect(); + let output = fn_abi.ret.layout.ty; + let mut fn_sig_kind = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); + let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); + let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + + let ignored = self + .sanitizer_ignorelist + .as_ref() + .is_some_and(|l| l.contains_prefix(c"kcfi", c"type", &type_name)); + + 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 8c9bf55b14e45..e18737fe49931 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> { @@ -2463,6 +2463,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 eb7a529c0b198..28136a058f16c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -375,6 +375,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 983a506bd4ac6..57a697c0f0182 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 @@ -1827,3 +1829,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 90459090ced87..27b9455727088 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2809,6 +2809,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() {} From 6728db4b41755cfb833dd6dd0e3d94dfea6c924d Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Mon, 15 Jun 2026 08:35:32 +0000 Subject: [PATCH 2/9] Refactor type_name_for_ignore_list into its own method --- compiler/rustc_codegen_llvm/src/builder.rs | 20 +++----------------- compiler/rustc_codegen_llvm/src/common.rs | 18 ++++++++++++++++-- compiler/rustc_codegen_llvm/src/declare.rs | 20 +++----------------- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7ba5de7ea7e5a..88c85b177c902 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -32,7 +32,7 @@ use tracing::{debug, instrument}; use crate::abi::FnAbiLlvmExt; use crate::attributes; -use crate::common::Funclet; +use crate::common::{type_name_for_ignore_list, Funclet}; use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; use crate::llvm::{ self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE, @@ -2021,14 +2021,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } - 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 = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); - let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); - let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + let type_name = type_name_for_ignore_list(self.tcx, fn_abi); let typeid = if let Some(instance) = instance { cfi::typeid_for_instance(self.tcx, instance, options) @@ -2157,14 +2150,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } - 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 = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); - let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); - let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + let type_name = type_name_for_ignore_list(self.tcx, fn_abi); if self.cx.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { ignorelist.contains_prefix(c"kcfi", c"type", &type_name) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 3fe4550b6759e..056ecf697fcdd 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -15,8 +15,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; -use rustc_middle::ty::{Instance, TyCtxt}; -use rustc_session::{PointerAuthAddressDiscriminator, PointerAuthSchema}; +use rustc_middle::ty::{self, Ty, Instance, TyCtxt}; +use rustc_session::{PointerAuthAddressDiscriminator, PointerAuthSchema, cstore::DllImport}; use tracing::debug; use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; @@ -535,3 +535,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/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 2636da108315b..729fb455e33bb 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -25,7 +25,7 @@ use tracing::debug; use crate::abi::FnAbiLlvmExt; use crate::attributes; -use crate::common::AsCCharPtr; +use crate::common::{type_name_for_ignore_list, AsCCharPtr}; use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx}; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, FromGeneric, Type, Value, Visibility}; @@ -193,14 +193,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_cfi_enabled() && !crate::llvm::HasStringAttribute(llfn, "no-sanitize-cfi") { - 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 = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); - let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); - let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + let type_name = type_name_for_ignore_list(self.tcx, fn_abi); if let Some(instance) = instance { let mut typeids = FxIndexSet::default(); @@ -254,14 +247,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } - 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 = rustc_middle::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 = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind); - let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig)); - let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string()); + let type_name = type_name_for_ignore_list(self.tcx, fn_abi); let ignored = self .sanitizer_ignorelist From c59286728ebf5bb82671a1cd3e653b1b9044e95e Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Mon, 15 Jun 2026 08:44:43 +0000 Subject: [PATCH 3/9] Refactor when tipeid is computed --- compiler/rustc_codegen_llvm/src/builder.rs | 12 ++--- compiler/rustc_codegen_llvm/src/declare.rs | 62 ++++++++++------------ 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 88c85b177c902..d965c31bb9ca5 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2023,12 +2023,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let type_name = type_name_for_ignore_list(self.tcx, fn_abi); - let typeid = if let Some(instance) = instance { - cfi::typeid_for_instance(self.tcx, instance, options) - } else { - cfi::typeid_for_fnabi(self.tcx, fn_abi, options) - }; - if self .cx .sanitizer_ignorelist @@ -2037,6 +2031,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { { return; } + + let typeid = if let Some(instance) = instance { + cfi::typeid_for_instance(self.tcx, instance, options) + } else { + cfi::typeid_for_fnabi(self.tcx, fn_abi, options) + }; let typeid_metadata = self.cx.create_metadata(typeid.as_bytes()); let dbg_loc = self.get_dbg_loc(); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 729fb455e33bb..a0dfbb2e79eb8 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -194,41 +194,37 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { && !crate::llvm::HasStringAttribute(llfn, "no-sanitize-cfi") { let type_name = type_name_for_ignore_list(self.tcx, fn_abi); + let ignored = self.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { + ignorelist.contains_prefix(c"cfi", c"type", &type_name) + }); - 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); - let ignored = self.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { - ignorelist.contains_prefix(c"cfi", c"type", &type_name) - }); - if !ignored && typeids.insert(typeid.clone()) { - self.add_type_metadata(llfn, typeid.as_bytes()); + 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); - let ignored = self - .sanitizer_ignorelist - .as_ref() - .is_some_and(|l| l.contains_prefix(c"cfi", c"type", &type_name)); - if !ignored { + } 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()); } } From 17ae23977b669499c8769095f42884c26344d9d8 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Mon, 15 Jun 2026 08:51:49 +0000 Subject: [PATCH 4/9] Move is_sanitizer_type_ignored to the context --- compiler/rustc_codegen_llvm/src/builder.rs | 74 +++++++++------------- compiler/rustc_codegen_llvm/src/context.rs | 11 ++++ compiler/rustc_codegen_llvm/src/declare.rs | 14 +--- 3 files changed, 45 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index d965c31bb9ca5..dae8b2d17e0e1 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -32,7 +32,7 @@ use tracing::{debug, instrument}; use crate::abi::FnAbiLlvmExt; use crate::attributes; -use crate::common::{type_name_for_ignore_list, Funclet}; +use crate::common::Funclet; use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; use crate::llvm::{ self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE, @@ -2021,14 +2021,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } - let type_name = type_name_for_ignore_list(self.tcx, fn_abi); - - if self - .cx - .sanitizer_ignorelist - .as_ref() - .is_some_and(|ignorelist| ignorelist.contains_prefix(c"cfi", c"type", &type_name)) - { + if self.cx.is_sanitizer_type_ignored(c"cfi", fn_abi) { return; } @@ -2128,46 +2121,41 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llfn: &'ll Value, ) -> Option> { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; - let kcfi_bundle = - if self.tcx.sess.is_sanitizer_kcfi_enabled() - && let Some(fn_abi) = fn_abi - && is_indirect_call + let kcfi_bundle = if self.tcx.sess.is_sanitizer_kcfi_enabled() + && let Some(fn_abi) = fn_abi + && is_indirect_call + { + if let Some(fn_attrs) = fn_attrs + && fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI) { - if let Some(fn_attrs) = fn_attrs - && fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI) - { - 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() { - options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); - } - if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); - } - - let type_name = type_name_for_ignore_list(self.tcx, fn_abi); + return None; + } + if crate::llvm::HasStringAttribute(self.llfn(), "no-sanitize-kcfi") { + return None; + } - if self.cx.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { - ignorelist.contains_prefix(c"kcfi", c"type", &type_name) - }) { - return None; - } + let mut options = kcfi::TypeIdOptions::empty(); + if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); + } + if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); + } - 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) - }; + if self.cx.is_sanitizer_type_ignored(c"kcfi", fn_abi) { + return None; + } - Some(llvm::OperandBundleBox::new("kcfi", &[self.const_u32(kcfi_typeid)])) + let kcfi_typeid = if let Some(instance) = instance { + kcfi::typeid_for_instance(self.tcx, instance, options) } else { - None + kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; + + Some(llvm::OperandBundleBox::new("kcfi", &[self.const_u32(kcfi_typeid)])) + } else { + None + }; kcfi_bundle } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f2c759050e863..24186ca5d1c8c 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -860,6 +860,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 a0dfbb2e79eb8..63276d1d3bec3 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -25,7 +25,7 @@ use tracing::debug; use crate::abi::FnAbiLlvmExt; use crate::attributes; -use crate::common::{type_name_for_ignore_list, AsCCharPtr}; +use crate::common::AsCCharPtr; use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx}; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, FromGeneric, Type, Value, Visibility}; @@ -193,10 +193,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_cfi_enabled() && !crate::llvm::HasStringAttribute(llfn, "no-sanitize-cfi") { - let type_name = type_name_for_ignore_list(self.tcx, fn_abi); - let ignored = self.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| { - ignorelist.contains_prefix(c"cfi", c"type", &type_name) - }); + let ignored = self.is_sanitizer_type_ignored(c"cfi", fn_abi); if !ignored { if let Some(instance) = instance { @@ -243,12 +240,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } - let type_name = type_name_for_ignore_list(self.tcx, fn_abi); - - let ignored = self - .sanitizer_ignorelist - .as_ref() - .is_some_and(|l| l.contains_prefix(c"kcfi", c"type", &type_name)); + let ignored = self.is_sanitizer_type_ignored(c"kcfi", fn_abi); if !ignored { let kcfi_typeid = if let Some(instance) = instance { From 066c9e132990d4d9f742cc7639e3ea83c3a3d2c1 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Thu, 27 Aug 2026 10:10:20 +0000 Subject: [PATCH 5/9] Move shared code to rustc_sanitizers crate and reorg tests --- Cargo.lock | 1 + compiler/rustc_codegen_llvm/src/attributes.rs | 58 +------- compiler/rustc_codegen_llvm/src/base.rs | 7 +- compiler/rustc_codegen_llvm/src/common.rs | 18 +-- compiler/rustc_codegen_llvm/src/context.rs | 9 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 13 -- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 46 ------ compiler/rustc_sanitizers/Cargo.toml | 1 + .../rustc_sanitizers/src/ignorelist/ffi.rs | 64 +++++++++ .../rustc_sanitizers/src/ignorelist/mod.rs | 136 ++++++++++++++++++ compiler/rustc_sanitizers/src/lib.rs | 3 + .../{ => ignorelist}/cfi-ignorelist.rs | 2 +- .../{ => ignorelist}/global-ignorelist.rs | 2 +- .../{ => ignorelist}/global-ignorelist.txt | 0 .../sanitizer/{ => ignorelist}/ignorelist.rs | 2 +- .../sanitizer/{ => ignorelist}/ignorelist.txt | 0 .../{ => ignorelist}/kcfi-ignorelist.rs | 2 +- .../kernel-address-ignorelist.rs | 2 +- .../kernel-address-ignorelist.txt | 0 .../{ => ignorelist}/mainfile-ignore.rs | 2 +- .../{ => ignorelist}/mainfile-ignorelist.txt | 0 .../{ => ignorelist}/override-ignorelist.rs | 2 +- .../{ => ignorelist}/override-ignorelist.txt | 0 .../{ => ignorelist}/src-ignore-memory.rs | 2 +- .../sanitizer/{ => ignorelist}/src-ignore.rs | 2 +- .../{ => ignorelist}/type-ignorelist-asan.rs | 2 +- .../{ => ignorelist}/type-ignorelist-kcfi.rs | 2 +- .../{ => ignorelist}/type-ignorelist.rs | 2 +- .../type-string-unsafe-ignorelist.txt | 0 .../{ => ignorelist}/type-string-unsafe.rs | 2 +- 30 files changed, 236 insertions(+), 146 deletions(-) create mode 100644 compiler/rustc_sanitizers/src/ignorelist/ffi.rs create mode 100644 compiler/rustc_sanitizers/src/ignorelist/mod.rs rename tests/codegen-llvm/sanitizer/{ => ignorelist}/cfi-ignorelist.rs (95%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/global-ignorelist.rs (87%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/global-ignorelist.txt (100%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/ignorelist.rs (96%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/ignorelist.txt (100%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/kcfi-ignorelist.rs (92%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/kernel-address-ignorelist.rs (88%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/kernel-address-ignorelist.txt (100%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/mainfile-ignore.rs (83%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/mainfile-ignorelist.txt (100%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/override-ignorelist.rs (88%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/override-ignorelist.txt (100%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/src-ignore-memory.rs (96%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/src-ignore.rs (93%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/type-ignorelist-asan.rs (91%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/type-ignorelist-kcfi.rs (93%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/type-ignorelist.rs (95%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/type-string-unsafe-ignorelist.txt (100%) rename tests/codegen-llvm/sanitizer/{ => ignorelist}/type-string-unsafe.rs (79%) diff --git a/Cargo.lock b/Cargo.lock index 696b797e612f9..0bc9f24943556 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4731,6 +4731,7 @@ name = "rustc_sanitizers" version = "0.0.0" dependencies = [ "bitflags", + "libc", "rustc_abi", "rustc_data_structures", "rustc_hir", diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 8f20cd966f849..60f7ba53d3ab4 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -7,6 +7,7 @@ use rustc_middle::middle::codegen_fn_attrs::{ TargetFeature, }; use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_sanitizers::ignorelist::SanitizerIgnoreList; use rustc_session::config::{ BranchProtection, FunctionReturn, InstrumentMcount, InstrumentMcountOpts, OptLevel, PAuthKey, PacRet, @@ -476,7 +477,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>, + sanitizer_ignorelist: Option<&SanitizerIgnoreList>, ) { let sess = tcx.sess; let mut to_add = SmallVec::<[_; 16]>::new(); @@ -541,59 +542,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( 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") { + let result = ignorelist.filter_instance_sanitizers(tcx, instance, enabled); + enabled = result.enabled; + if result.ignore_cfi { to_add.push(llvm::CreateAttrString(cx.llcx, "no-sanitize-cfi")); } - if is_ignored(c"kcfi") { + if result.ignore_kcfi { to_add.push(llvm::CreateAttrString(cx.llcx, "no-sanitize-kcfi")); } } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 30c5de39c25d5..a718e936b70da 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -129,7 +129,12 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default(), tcx.sess.sanitizers()); + let mut attrs = attributes::sanitize_attrs( + &cx, + tcx, + SanitizerFnAttrs::default(), + tcx.sess.sanitizers(), + ); // When pointer authentication is enabled, ensure that the ptrauth-* attributes are // also attached to the entry wrapper. // diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 056ecf697fcdd..3fe4550b6759e 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -15,8 +15,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; -use rustc_middle::ty::{self, Ty, Instance, TyCtxt}; -use rustc_session::{PointerAuthAddressDiscriminator, PointerAuthSchema, cstore::DllImport}; +use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_session::{PointerAuthAddressDiscriminator, PointerAuthSchema}; use tracing::debug; use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; @@ -535,17 +535,3 @@ 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/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 24186ca5d1c8c..a1888ff18f594 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; +use rustc_sanitizers::ignorelist::{SanitizerIgnoreList, type_name_for_ignore_list}; use rustc_session::config::{ BranchProtection, CFGuard, CFProtection, DebugInfo, FunctionReturn, PAuthKey, PacRet, }; @@ -133,7 +134,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, + pub sanitizer_ignorelist: Option, eh_personality: Cell>, pub rust_try_fn: Cell>, @@ -689,9 +690,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { 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, - ) { + match 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)); @@ -867,7 +866,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { 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); + let type_name = type_name_for_ignore_list(self.tcx, fn_abi); ignorelist.contains_prefix(sanitizer, c"type", &type_name) }) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e18737fe49931..453053b197dda 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2463,19 +2463,6 @@ 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 28136a058f16c..eb7a529c0b198 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -375,52 +375,6 @@ 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_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml index 8eff14d0cfcfa..75270e00ecc5b 100644 --- a/compiler/rustc_sanitizers/Cargo.toml +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.5.0" +libc = "0.2" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_sanitizers/src/ignorelist/ffi.rs b/compiler/rustc_sanitizers/src/ignorelist/ffi.rs new file mode 100644 index 0000000000000..f99489adff129 --- /dev/null +++ b/compiler/rustc_sanitizers/src/ignorelist/ffi.rs @@ -0,0 +1,64 @@ +use std::cell::RefCell; +use std::ffi::c_char; +use std::ptr; +use std::string::FromUtf8Error; + +use libc::size_t; + +unsafe extern "C" { + pub(crate) type Opaque; + /// Opaque type that allows C++ code to write bytes to a Rust-side buffer, + /// in conjunction with `RawRustStringOstream`. Use this as `&RustString` + /// (Rust) and `RustStringRef` (C++) in FFI signatures. + pub(crate) type RustString; + + 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; +} + +/// Underlying implementation of [`RustString`]. +/// +/// Having two separate types makes it possible to use the opaque [`RustString`] +/// in FFI signatures without `improper_ctypes` warnings. This is a workaround +/// for the fact that there is no way to opt out of `improper_ctypes` when +/// _declaring_ a type (as opposed to using that type). +#[derive(Default)] +struct RustStringInner { + bytes: RefCell>, +} + +impl RustStringInner { + fn as_opaque(&self) -> &RustString { + let ptr: *const RustStringInner = ptr::from_ref(self); + // We can't use `ptr::cast` here because extern types are `!Sized`. + let ptr = ptr as *const RustString; + unsafe { &*ptr } + } + + fn into_inner(self) -> Vec { + self.bytes.into_inner() + } +} + +impl RustString { + pub(crate) fn build_byte_buffer(closure: impl FnOnce(&Self)) -> Vec { + let buf = RustStringInner::default(); + closure(buf.as_opaque()); + buf.into_inner() + } +} + +pub(crate) fn build_string(f: impl FnOnce(&RustString)) -> Result { + String::from_utf8(RustString::build_byte_buffer(f)) +} diff --git a/compiler/rustc_sanitizers/src/ignorelist/mod.rs b/compiler/rustc_sanitizers/src/ignorelist/mod.rs new file mode 100644 index 0000000000000..ee8f4768cacaf --- /dev/null +++ b/compiler/rustc_sanitizers/src/ignorelist/mod.rs @@ -0,0 +1,136 @@ +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_target::spec::SanitizerSet; + +pub(crate) mod ffi; + +pub struct SanitizerIgnoreList { + inner: *mut ffi::Opaque, +} + +#[derive(Clone, Copy, Debug)] +pub struct InstanceSanitizers { + pub enabled: SanitizerSet, + pub ignore_cfi: bool, + pub ignore_kcfi: bool, +} + +impl SanitizerIgnoreList { + pub 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 = ffi::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 fn is_instance_ignored<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + section: &std::ffi::CStr, + ) -> bool { + let sym_name = tcx.symbol_name(instance).name; + let span = tcx.def_span(instance.def_id()); + let filename = + tcx.sess.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())); + + self.contains_prefix(section, c"fun", sym_name) + || self.contains_prefix(section, c"fun", &demangled) + || self.contains_prefix(section, c"src", &filename) + || (!mainfile.is_empty() && self.contains_prefix(section, c"mainfile", &mainfile)) + } + + pub fn filter_instance_sanitizers<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + mut enabled: SanitizerSet, + ) -> InstanceSanitizers { + let is_ignored = |section| self.is_instance_ignored(tcx, instance, section); + + 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); + } + + let ignore_cfi = is_ignored(c"cfi"); + let ignore_kcfi = is_ignored(c"kcfi"); + + InstanceSanitizers { enabled, ignore_cfi, ignore_kcfi } + } + + pub 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 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_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs index 7d7c1c8284db6..e6ccd2f02c154 100644 --- a/compiler/rustc_sanitizers/src/lib.rs +++ b/compiler/rustc_sanitizers/src/lib.rs @@ -3,8 +3,11 @@ //! This crate contains the source code for providing support for the sanitizers to the Rust //! compiler. +#![feature(extern_types)] + // tidy-alphabetical-start // tidy-alphabetical-end pub mod cfi; +pub mod ignorelist; pub mod kcfi; diff --git a/tests/codegen-llvm/sanitizer/cfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs similarity index 95% rename from tests/codegen-llvm/sanitizer/cfi-ignorelist.rs rename to tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs index 8871e7f0dcd39..568593e3006d2 100644 --- a/tests/codegen-llvm/sanitizer/cfi-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-cfi -//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/global-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.rs similarity index 87% rename from tests/codegen-llvm/sanitizer/global-ignorelist.rs rename to tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.rs index 994dccd834f1c..a59735921e9e1 100644 --- a/tests/codegen-llvm/sanitizer/global-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/global-ignorelist.txt +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/global-ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/global-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.txt similarity index 100% rename from tests/codegen-llvm/sanitizer/global-ignorelist.txt rename to tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.txt diff --git a/tests/codegen-llvm/sanitizer/ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/ignorelist.rs similarity index 96% rename from tests/codegen-llvm/sanitizer/ignorelist.rs rename to tests/codegen-llvm/sanitizer/ignorelist/ignorelist.rs index d71a17b4b3c1b..a589d24134c11 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/ignorelist.rs @@ -4,7 +4,7 @@ //@[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 +//@ compile-flags: -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt -Cunsafe-allow-abi-mismatch=sanitizer //@ [ASAN] compile-flags: -Zsanitizer=address //@ [MSAN] compile-flags: -Zsanitizer=memory //@ [TSAN] compile-flags: -Zsanitizer=thread diff --git a/tests/codegen-llvm/sanitizer/ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/ignorelist.txt similarity index 100% rename from tests/codegen-llvm/sanitizer/ignorelist.txt rename to tests/codegen-llvm/sanitizer/ignorelist/ignorelist.txt diff --git a/tests/codegen-llvm/sanitizer/kcfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs similarity index 92% rename from tests/codegen-llvm/sanitizer/kcfi-ignorelist.rs rename to tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs index 207a561714b78..25ec87fd7135b 100644 --- a/tests/codegen-llvm/sanitizer/kcfi-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-kcfi -//@ compile-flags: -Zsanitizer=kcfi -C panic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt +//@ compile-flags: -Zsanitizer=kcfi -C panic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.rs similarity index 88% rename from tests/codegen-llvm/sanitizer/kernel-address-ignorelist.rs rename to tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.rs index c6a59365f1671..293a261daa0e8 100644 --- a/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/kernel-address-ignorelist.txt +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/kernel-address-ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kernel-address-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.txt similarity index 100% rename from tests/codegen-llvm/sanitizer/kernel-address-ignorelist.txt rename to tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.txt diff --git a/tests/codegen-llvm/sanitizer/mainfile-ignore.rs b/tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignore.rs similarity index 83% rename from tests/codegen-llvm/sanitizer/mainfile-ignore.rs rename to tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignore.rs index 683f739c53be2..b59ff94e054ed 100644 --- a/tests/codegen-llvm/sanitizer/mainfile-ignore.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignore.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/mainfile-ignorelist.txt +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/mainfile-ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/mainfile-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignorelist.txt similarity index 100% rename from tests/codegen-llvm/sanitizer/mainfile-ignorelist.txt rename to tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignorelist.txt diff --git a/tests/codegen-llvm/sanitizer/override-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs similarity index 88% rename from tests/codegen-llvm/sanitizer/override-ignorelist.rs rename to tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs index 74743cfc27358..497bb852af06a 100644 --- a/tests/codegen-llvm/sanitizer/override-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/override-ignorelist.txt +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/override-ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/override-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.txt similarity index 100% rename from tests/codegen-llvm/sanitizer/override-ignorelist.txt rename to tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.txt diff --git a/tests/codegen-llvm/sanitizer/src-ignore-memory.rs b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore-memory.rs similarity index 96% rename from tests/codegen-llvm/sanitizer/src-ignore-memory.rs rename to tests/codegen-llvm/sanitizer/ignorelist/src-ignore-memory.rs index 9799e9b522f03..8c533a6cc53ab 100644 --- a/tests/codegen-llvm/sanitizer/src-ignore-memory.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore-memory.rs @@ -4,7 +4,7 @@ //@[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 +//@ compile-flags: -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt -Cunsafe-allow-abi-mismatch=sanitizer //@ [ASAN] compile-flags: -Zsanitizer=address //@ [MSAN] compile-flags: -Zsanitizer=memory //@ [TSAN] compile-flags: -Zsanitizer=thread diff --git a/tests/codegen-llvm/sanitizer/src-ignore.rs b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs similarity index 93% rename from tests/codegen-llvm/sanitizer/src-ignore.rs rename to tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs index dfacb473c8ab8..fadffa7773c5f 100644 --- a/tests/codegen-llvm/sanitizer/src-ignore.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-cfi -//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/type-ignorelist-asan.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-asan.rs similarity index 91% rename from tests/codegen-llvm/sanitizer/type-ignorelist-asan.rs rename to tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-asan.rs index 6f04e863fb522..65c441f9861d6 100644 --- a/tests/codegen-llvm/sanitizer/type-ignorelist-asan.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-asan.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/type-ignorelist-kcfi.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs similarity index 93% rename from tests/codegen-llvm/sanitizer/type-ignorelist-kcfi.rs rename to tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs index 8010ce19be25a..6812c764981bb 100644 --- a/tests/codegen-llvm/sanitizer/type-ignorelist-kcfi.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-kcfi -//@ compile-flags: -Zsanitizer=kcfi -Cpanic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt +//@ compile-flags: -Zsanitizer=kcfi -Cpanic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/type-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs similarity index 95% rename from tests/codegen-llvm/sanitizer/type-ignorelist.rs rename to tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs index 6ddbb709b2d98..6ae61339fa513 100644 --- a/tests/codegen-llvm/sanitizer/type-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-cfi -//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist.txt +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/type-string-unsafe-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe-ignorelist.txt similarity index 100% rename from tests/codegen-llvm/sanitizer/type-string-unsafe-ignorelist.txt rename to tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe-ignorelist.txt diff --git a/tests/codegen-llvm/sanitizer/type-string-unsafe.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe.rs similarity index 79% rename from tests/codegen-llvm/sanitizer/type-string-unsafe.rs rename to tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe.rs index f4e7b83bf61b0..f7f9d7538f78c 100644 --- a/tests/codegen-llvm/sanitizer/type-string-unsafe.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/type-string-unsafe-ignorelist.txt +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/type-string-unsafe-ignorelist.txt #![crate_type = "lib"] From 53635b36215fd4151d531a8db715f4f30e8e1841 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Thu, 27 Aug 2026 15:13:43 +0000 Subject: [PATCH 6/9] Always emit type metadata for CFI and KCFI --- compiler/rustc_codegen_llvm/src/declare.rs | 78 ++++++++----------- .../sanitizer/ignorelist/cfi-ignorelist.rs | 7 +- .../sanitizer/ignorelist/kcfi-ignorelist.rs | 11 ++- .../sanitizer/ignorelist/src-ignore.rs | 6 +- .../ignorelist/type-ignorelist-kcfi.rs | 9 ++- .../sanitizer/ignorelist/type-ignorelist.rs | 11 +-- 6 files changed, 61 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 63276d1d3bec3..3511056f79306 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -190,47 +190,39 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { ); fn_abi.apply_attrs_llfn(self, llfn, instance); - 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); + 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()) { 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() - && !crate::llvm::HasStringAttribute(llfn, "no-sanitize-kcfi") - { + if self.tcx.sess.is_sanitizer_kcfi_enabled() { // 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() { @@ -240,16 +232,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } - 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); - } + 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); } llfn diff --git a/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs index 568593e3006d2..a8a64878499bb 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs @@ -4,19 +4,20 @@ #![crate_type = "lib"] // CHECK: define void @test_cfi -// CHECK-NOT: !type +// CHECK-SAME: !type +// CHECK-NOT: trap +// CHECK: call void %f() #[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 +// CHECK: trap #[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/ignorelist/kcfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs index 25ec87fd7135b..72eef9d21513f 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs @@ -4,15 +4,20 @@ #![crate_type = "lib"] // CHECK: define void @test_kcfi -// CHECK-NOT: !kcfi_type +// CHECK-SAME: !kcfi_type +// CHECK-NOT: [ "kcfi" +// CHECK: call void %f() #[no_mangle] -pub fn test_kcfi(x: &mut i32) { +pub fn test_kcfi(f: fn(), x: &mut i32) { *x = 1; + f(); } // CHECK: define void @test_memory // CHECK-SAME: !kcfi_type +// CHECK: call void %f(i32 {{.*}}1){{.*}}[ "kcfi" #[no_mangle] -pub fn test_memory(x: &mut i32) { +pub fn test_memory(f: fn(i32), x: &mut i32) { *x = 2; + f(1); } diff --git a/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs index fadffa7773c5f..0c57340326997 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs @@ -4,10 +4,12 @@ #![crate_type = "lib"] // CHECK: define void @test_file -// CHECK-NOT: !type +// CHECK-SAME: !type +// CHECK-NOT: llvm.type.test +// CHECK-NOT: trap +// CHECK: call void %f() #[no_mangle] pub fn test_file(f: fn(), x: &mut i32) { *x = 1; - // CHECK-NOT: trap f(); } diff --git a/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs index 6812c764981bb..5546caba1772f 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs @@ -5,14 +5,17 @@ // CHECK: define void @test_type // CHECK-SAME: !kcfi_type +// CHECK-NOT: [ "kcfi" +// CHECK: call void %f() +// CHECK: call void %g(i32 {{.*}}1){{.*}}[ "kcfi" #[no_mangle] -pub fn test_type(f: fn(), x: &mut i32) { +pub fn test_type(f: fn(), g: fn(i32), x: &mut i32) { *x = 1; - // CHECK-NOT: !kcfi_type f(); + g(1); } // CHECK: define void @test_type_2() -// CHECK-NOT: !kcfi_type +// CHECK-SAME: !kcfi_type #[no_mangle] pub fn test_type_2() {} diff --git a/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs index 6ae61339fa513..20904138a29bc 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs @@ -5,16 +5,17 @@ // CHECK: define void @test_type // CHECK-SAME: !type +// CHECK-NOT: trap +// CHECK: call void %f() +// CHECK: trap #[no_mangle] -pub fn test_type(f: fn(), x: &mut i32) { +pub fn test_type(f: fn(), g: fn(i32), x: &mut i32) { *x = 1; - // CHECK-NOT: trap f(); + g(1); } -// 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 +// CHECK-SAME: !type #[no_mangle] pub fn test_type_2() {} From 192c83809a638c384f60db0d21a2cc35da45d8d3 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Thu, 27 Aug 2026 15:46:17 +0000 Subject: [PATCH 7/9] sanitizers: track SpecialCaseList blame to resolve ignorelist overrides --- compiler/rustc_codegen_llvm/src/consts.rs | 45 ++++++-- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 23 ++-- .../rustc_sanitizers/src/ignorelist/ffi.rs | 27 ++++- .../rustc_sanitizers/src/ignorelist/mod.rs | 108 +++++++++++++----- .../ignorelist/override-ignorelist.rs | 10 ++ .../ignorelist/override-ignorelist.txt | 1 + .../ignorelist/override-kernel-address.rs | 22 ++++ .../ignorelist/override-kernel-address.txt | 6 + 8 files changed, 197 insertions(+), 45 deletions(-) create mode 100644 tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.rs create mode 100644 tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.txt diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index b9e5205381eef..218a5e3a12db7 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -587,19 +587,44 @@ impl<'ll> CodegenCx<'ll, '_> { .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 demangled = rustc_middle::ty::print::with_no_trimmed_paths!( + self.tcx.def_path_str(def_id) + ); + + let global_blame = |section: &std::ffi::CStr| -> (rustc_sanitizers::ignorelist::Blame, rustc_sanitizers::ignorelist::Blame) { + let mut no_san = rustc_sanitizers::ignorelist::Blame::NONE; + let mut san = rustc_sanitizers::ignorelist::Blame::NONE; + let mut update = |prefix: &std::ffi::CStr, query: &str| { + let (ns, s) = ignorelist.in_section_blame(section, prefix, query); + no_san = no_san.max(ns); + san = san.max(s); + }; + update(c"global", sym_name); + update(c"global", &demangled); + update(c"src", &filename); + if !mainfile.is_empty() { + update(c"mainfile", &mainfile); + } + update(c"type", &ty_name); + (no_san, san) }; 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"); + let (address_nosan, address_san) = global_blame(c"address"); + let (kaddress_nosan, kaddress_san) = global_blame(c"kernel-address"); + let (hwaddress_nosan, hwaddress_san) = global_blame(c"hwaddress"); + let (khwaddress_nosan, khwaddress_san) = global_blame(c"kernel-hwaddress"); + + let ignore_address = rustc_sanitizers::ignorelist::is_blame_ignored(address_nosan, address_san); + let ignore_kernel_address = rustc_sanitizers::ignorelist::is_blame_ignored( + address_nosan.max(kaddress_nosan), + address_san.max(kaddress_san), + ); + let ignore_hwaddress = rustc_sanitizers::ignorelist::is_blame_ignored(hwaddress_nosan, hwaddress_san); + let ignore_kernel_hwaddress = rustc_sanitizers::ignorelist::is_blame_ignored( + hwaddress_nosan.max(khwaddress_nosan), + hwaddress_san.max(khwaddress_san), + ); if (sanitizers.contains(rustc_target::spec::SanitizerSet::ADDRESS) && ignore_address) || (sanitizers.contains(rustc_target::spec::SanitizerSet::KERNELADDRESS) diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 57a697c0f0182..d7c3e25603306 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1848,16 +1848,25 @@ 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) { +struct LLVMRustSpecialCaseListBlame { + uint32_t FileIdx; + uint32_t LineNo; +}; + +extern "C" void +LLVMRustSpecialCaseListInSectionBlame(LLVMSpecialCaseListRef List, + const char *Section, const char *Prefix, + const char *Query, + LLVMRustSpecialCaseListBlame *OutNoSan, + LLVMRustSpecialCaseListBlame *OutSan) { auto *SCL = reinterpret_cast(List); std::pair NoSan = SCL->inSectionBlame(Section, Prefix, Query); - if (NoSan.second == 0) - return false; + OutNoSan->FileIdx = NoSan.first; + OutNoSan->LineNo = NoSan.second; + std::pair San = SCL->inSectionBlame(Section, Prefix, Query, "sanitize"); - return San.second == 0 || NoSan > San; + OutSan->FileIdx = San.first; + OutSan->LineNo = San.second; } diff --git a/compiler/rustc_sanitizers/src/ignorelist/ffi.rs b/compiler/rustc_sanitizers/src/ignorelist/ffi.rs index f99489adff129..abf86b28dd1c3 100644 --- a/compiler/rustc_sanitizers/src/ignorelist/ffi.rs +++ b/compiler/rustc_sanitizers/src/ignorelist/ffi.rs @@ -5,6 +5,27 @@ use std::string::FromUtf8Error; use libc::size_t; +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct Blame { + pub file_idx: u32, + pub line_no: u32, +} + +impl Blame { + pub const NONE: Self = Self { file_idx: 0, line_no: 0 }; + + #[inline] + pub fn is_none(self) -> bool { + self.line_no == 0 + } + + #[inline] + pub fn is_some(self) -> bool { + self.line_no != 0 + } +} + unsafe extern "C" { pub(crate) type Opaque; /// Opaque type that allows C++ code to write bytes to a Rust-side buffer, @@ -19,12 +40,14 @@ unsafe extern "C" { ) -> *mut Opaque; pub(crate) fn LLVMRustSpecialCaseListDestroy(List: *mut Opaque); - pub(crate) fn LLVMRustSpecialCaseListContainsPrefix( + pub(crate) fn LLVMRustSpecialCaseListInSectionBlame( List: *const Opaque, Section: *const c_char, Prefix: *const c_char, Query: *const c_char, - ) -> bool; + OutNoSan: *mut Blame, + OutSan: *mut Blame, + ); } /// Underlying implementation of [`RustString`]. diff --git a/compiler/rustc_sanitizers/src/ignorelist/mod.rs b/compiler/rustc_sanitizers/src/ignorelist/mod.rs index ee8f4768cacaf..edb01da24037a 100644 --- a/compiler/rustc_sanitizers/src/ignorelist/mod.rs +++ b/compiler/rustc_sanitizers/src/ignorelist/mod.rs @@ -1,8 +1,15 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_target::spec::SanitizerSet; +pub use ffi::Blame; + pub(crate) mod ffi; +#[inline] +pub fn is_blame_ignored(no_san: Blame, san: Blame) -> bool { + no_san.is_some() && (san.is_none() || no_san > san) +} + pub struct SanitizerIgnoreList { inner: *mut ffi::Opaque, } @@ -30,12 +37,36 @@ impl SanitizerIgnoreList { if inner.is_null() { Err(err) } else { Ok(Self { inner }) } } - pub fn is_instance_ignored<'tcx>( + pub fn in_section_blame( + &self, + section: &std::ffi::CStr, + prefix: &std::ffi::CStr, + query: &str, + ) -> (Blame, Blame) { + let mut no_san = Blame::NONE; + let mut san = Blame::NONE; + let Ok(query) = std::ffi::CString::new(query) else { + return (Blame::NONE, Blame::NONE); + }; + unsafe { + ffi::LLVMRustSpecialCaseListInSectionBlame( + self.inner, + section.as_ptr(), + prefix.as_ptr(), + query.as_ptr(), + &mut no_san, + &mut san, + ); + } + (no_san, san) + } + + pub fn instance_blame<'tcx>( &self, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, section: &std::ffi::CStr, - ) -> bool { + ) -> (Blame, Blame) { let sym_name = tcx.symbol_name(instance).name; let span = tcx.def_span(instance.def_id()); let filename = @@ -48,10 +79,32 @@ impl SanitizerIgnoreList { let demangled = rustc_middle::ty::print::with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())); - self.contains_prefix(section, c"fun", sym_name) - || self.contains_prefix(section, c"fun", &demangled) - || self.contains_prefix(section, c"src", &filename) - || (!mainfile.is_empty() && self.contains_prefix(section, c"mainfile", &mainfile)) + let mut no_san = Blame::NONE; + let mut san = Blame::NONE; + let mut update = |prefix: &std::ffi::CStr, query: &str| { + let (ns, s) = self.in_section_blame(section, prefix, query); + no_san = no_san.max(ns); + san = san.max(s); + }; + + update(c"fun", sym_name); + update(c"fun", &demangled); + update(c"src", &filename); + if !mainfile.is_empty() { + update(c"mainfile", &mainfile); + } + + (no_san, san) + } + + pub fn is_instance_ignored<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + section: &std::ffi::CStr, + ) -> bool { + let (no_san, san) = self.instance_blame(tcx, instance, section); + is_blame_ignored(no_san, san) } pub fn filter_instance_sanitizers<'tcx>( @@ -60,12 +113,22 @@ impl SanitizerIgnoreList { instance: Instance<'tcx>, mut enabled: SanitizerSet, ) -> InstanceSanitizers { - let is_ignored = |section| self.is_instance_ignored(tcx, instance, section); - - 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"); + let (address_nosan, address_san) = self.instance_blame(tcx, instance, c"address"); + let (kaddress_nosan, kaddress_san) = self.instance_blame(tcx, instance, c"kernel-address"); + let (hwaddress_nosan, hwaddress_san) = self.instance_blame(tcx, instance, c"hwaddress"); + let (khwaddress_nosan, khwaddress_san) = self.instance_blame(tcx, instance, c"kernel-hwaddress"); + + let ignore_address = is_blame_ignored(address_nosan, address_san); + let ignore_kernel_address = is_blame_ignored( + address_nosan.max(kaddress_nosan), + address_san.max(kaddress_san), + ); + + let ignore_hwaddress = is_blame_ignored(hwaddress_nosan, hwaddress_san); + let ignore_kernel_hwaddress = is_blame_ignored( + hwaddress_nosan.max(khwaddress_nosan), + hwaddress_san.max(khwaddress_san), + ); if enabled.contains(SanitizerSet::ADDRESS) && ignore_address { enabled.remove(SanitizerSet::ADDRESS); @@ -73,10 +136,10 @@ impl SanitizerIgnoreList { if enabled.contains(SanitizerSet::KERNELADDRESS) && ignore_kernel_address { enabled.remove(SanitizerSet::KERNELADDRESS); } - if enabled.contains(SanitizerSet::MEMORY) && is_ignored(c"memory") { + if enabled.contains(SanitizerSet::MEMORY) && self.is_instance_ignored(tcx, instance, c"memory") { enabled.remove(SanitizerSet::MEMORY); } - if enabled.contains(SanitizerSet::THREAD) && is_ignored(c"thread") { + if enabled.contains(SanitizerSet::THREAD) && self.is_instance_ignored(tcx, instance, c"thread") { enabled.remove(SanitizerSet::THREAD); } if enabled.contains(SanitizerSet::HWADDRESS) && ignore_hwaddress { @@ -85,12 +148,12 @@ impl SanitizerIgnoreList { if enabled.contains(SanitizerSet::KERNELHWADDRESS) && ignore_kernel_hwaddress { enabled.remove(SanitizerSet::KERNELHWADDRESS); } - if enabled.contains(SanitizerSet::SAFESTACK) && is_ignored(c"safestack") { + if enabled.contains(SanitizerSet::SAFESTACK) && self.is_instance_ignored(tcx, instance, c"safestack") { enabled.remove(SanitizerSet::SAFESTACK); } - let ignore_cfi = is_ignored(c"cfi"); - let ignore_kcfi = is_ignored(c"kcfi"); + let ignore_cfi = self.is_instance_ignored(tcx, instance, c"cfi"); + let ignore_kcfi = self.is_instance_ignored(tcx, instance, c"kcfi"); InstanceSanitizers { enabled, ignore_cfi, ignore_kcfi } } @@ -101,15 +164,8 @@ impl SanitizerIgnoreList { 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(), - ) - } + let (no_san, san) = self.in_section_blame(section, prefix, query); + is_blame_ignored(no_san, san) } } diff --git a/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs index 497bb852af06a..63c9f9bbebf53 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs +++ b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs @@ -18,3 +18,13 @@ pub fn test_ignored(x: &mut i32) { pub fn test_re_enabled(x: &mut i32) { *x = 2; } + +pub static RE_ENABLED_REF: fn(&mut i32) = test_mangled_re_enabled; + +// CHECK: ; Function Attrs: +// CHECK-SAME: sanitize_address +// CHECK-LABEL: define {{.*}}test_mangled_re_enabled +#[inline(never)] +pub fn test_mangled_re_enabled(x: &mut i32) { + *x = 3; +} diff --git a/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.txt index 77d66c8341bce..5bcf5a1e5c398 100644 --- a/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.txt +++ b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.txt @@ -1,3 +1,4 @@ [address] fun:* fun:test_re_enabled=sanitize +fun:*test_mangled_re_enabled*=sanitize diff --git a/tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.rs b/tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.rs new file mode 100644 index 0000000000000..62942d15354b1 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.rs @@ -0,0 +1,22 @@ +//@ needs-sanitizer-kasan +//@ compile-flags: -Zsanitizer=kernel-address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/override-kernel-address.txt + +#![crate_type = "lib"] + +// Ignored via [address] fallback: +// CHECK: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK-NEXT: define void @test_fallback_ignored +#[no_mangle] +pub fn test_fallback_ignored(x: &mut i32) { + *x = 1; +} + +// Re-enabled via [kernel-address] =sanitize overriding [address]: +// CHECK: ; Function Attrs: +// CHECK-SAME: sanitize_address +// CHECK-NEXT: define void @test_kernel_override +#[no_mangle] +pub fn test_kernel_override(x: &mut i32) { + *x = 2; +} diff --git a/tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.txt b/tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.txt new file mode 100644 index 0000000000000..3696b9f56bf1b --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/override-kernel-address.txt @@ -0,0 +1,6 @@ +[address] +fun:test_kernel_override +fun:test_fallback_ignored + +[kernel-address] +fun:test_kernel_override=sanitize From 04e33d508f87431e0768a2b1a95989ba097dd3ba Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Thu, 27 Aug 2026 16:04:40 +0000 Subject: [PATCH 8/9] sanitizers: support Clang section names and glob expansion in ignorelist --- compiler/rustc_codegen_llvm/src/consts.rs | 16 ++- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 115 +++++++++++++++++- .../rustc_sanitizers/src/ignorelist/ffi.rs | 1 + .../rustc_sanitizers/src/ignorelist/mod.rs | 71 +++++++++-- 4 files changed, 181 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 218a5e3a12db7..3053125b026f7 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -587,11 +587,13 @@ impl<'ll> CodegenCx<'ll, '_> { .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!( - self.tcx.def_path_str(def_id) - ); + let demangled = + rustc_middle::ty::print::with_no_trimmed_paths!(self.tcx.def_path_str(def_id)); - let global_blame = |section: &std::ffi::CStr| -> (rustc_sanitizers::ignorelist::Blame, rustc_sanitizers::ignorelist::Blame) { + let global_blame = |section: &std::ffi::CStr| -> ( + rustc_sanitizers::ignorelist::Blame, + rustc_sanitizers::ignorelist::Blame, + ) { let mut no_san = rustc_sanitizers::ignorelist::Blame::NONE; let mut san = rustc_sanitizers::ignorelist::Blame::NONE; let mut update = |prefix: &std::ffi::CStr, query: &str| { @@ -615,12 +617,14 @@ impl<'ll> CodegenCx<'ll, '_> { let (hwaddress_nosan, hwaddress_san) = global_blame(c"hwaddress"); let (khwaddress_nosan, khwaddress_san) = global_blame(c"kernel-hwaddress"); - let ignore_address = rustc_sanitizers::ignorelist::is_blame_ignored(address_nosan, address_san); + let ignore_address = + rustc_sanitizers::ignorelist::is_blame_ignored(address_nosan, address_san); let ignore_kernel_address = rustc_sanitizers::ignorelist::is_blame_ignored( address_nosan.max(kaddress_nosan), address_san.max(kaddress_san), ); - let ignore_hwaddress = rustc_sanitizers::ignorelist::is_blame_ignored(hwaddress_nosan, hwaddress_san); + let ignore_hwaddress = + rustc_sanitizers::ignorelist::is_blame_ignored(hwaddress_nosan, hwaddress_san); let ignore_kernel_hwaddress = rustc_sanitizers::ignorelist::is_blame_ignored( hwaddress_nosan.max(khwaddress_nosan), hwaddress_san.max(khwaddress_san), diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index d7c3e25603306..9da80fcdc43ad 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1830,13 +1830,114 @@ FIXED_MD_KIND(MD_noalias_addrspace, 41) // code cannot declare them as fixed IDs and must look them up by name instead. #undef FIXED_MD_KIND +class RustSanitizerSpecialCaseList : public llvm::SpecialCaseList { +public: + static std::unique_ptr + create(const std::vector &Paths, llvm::vfs::FileSystem &VFS, + std::string &Error) { + std::unique_ptr SSCL( + new RustSanitizerSpecialCaseList()); + if (SSCL->createInternal(Paths, VFS, Error)) { + SSCL->createSanitizerSections(); + return SSCL; + } + return nullptr; + } + + std::pair + inSectionBlame(uint32_t Mask, llvm::StringRef SectionName, llvm::StringRef Prefix, + llvm::StringRef Query, llvm::StringRef Category = llvm::StringRef()) const { + for (auto It = SanitizerSections.rbegin(); It != SanitizerSections.rend(); ++It) { + bool Matches = false; + if (Mask != 0 && (It->Mask & Mask) != 0) { + Matches = true; + } else if (!SectionName.empty() && It->S.matchName(SectionName)) { + Matches = true; + } + if (Matches) { + unsigned LineNum = It->S.getLastMatch(Prefix, Query, Category); + if (LineNum > 0) + return {It->S.fileIndex(), LineNum}; + } + } + return NotFound; + } + +private: + struct SanitizerSection { + uint32_t Mask; + const Section &S; + SanitizerSection(uint32_t Mask, const Section &S) : Mask(Mask), S(S) {} + }; + + std::vector SanitizerSections; + + void createSanitizerSections() { + for (const auto &S : sections()) { + uint32_t Mask = 0; + + // Address: [address] + if (S.matchName("address")) + Mask |= (1 << 0); + // Leak: [leak] + if (S.matchName("leak")) + Mask |= (1 << 1); + // Memory: [memory] + if (S.matchName("memory")) + Mask |= (1 << 2); + // Thread: [thread] + if (S.matchName("thread")) + Mask |= (1 << 3); + // HWAddress: [hwaddress] + if (S.matchName("hwaddress")) + Mask |= (1 << 4); + + // CFI and its sub-kinds: [cfi], [cfi-icall], [cfi-vcall], [{cfi-vcall,cfi-icall}], etc. + if (S.matchName("cfi") || S.matchName("cfi-icall") || + S.matchName("cfi-vcall") || S.matchName("cfi-nvcall") || + S.matchName("cfi-derived-cast") || S.matchName("cfi-unrelated-cast") || + S.matchName("cfi-mfcall")) + Mask |= (1 << 5); + + // MemTag: [memtag], [memtag-stack], [memtag-heap], [memtag-globals] + if (S.matchName("memtag") || S.matchName("memtag-stack") || + S.matchName("memtag-heap") || S.matchName("memtag-globals")) + Mask |= (1 << 6); + // ShadowCallStack: [shadow-call-stack], [shadowcallstack] + if (S.matchName("shadow-call-stack") || S.matchName("shadowcallstack")) + Mask |= (1 << 7); + // KCFI: [kcfi] + if (S.matchName("kcfi")) + Mask |= (1 << 8); + // KernelAddress: [kernel-address], [kasan] + if (S.matchName("kernel-address") || S.matchName("kasan")) + Mask |= (1 << 9); + // KernelHWAddress: [kernel-hwaddress], [khwasan] + if (S.matchName("kernel-hwaddress") || S.matchName("khwasan")) + Mask |= (1 << 10); + // SafeStack: [safe-stack] (Clang standard), [safestack] + if (S.matchName("safe-stack") || S.matchName("safestack")) + Mask |= (1 << 11); + // DataFlow: [dataflow] + if (S.matchName("dataflow")) + Mask |= (1 << 12); + // Realtime: [realtime] + if (S.matchName("realtime")) + Mask |= (1 << 13); + + SanitizerSections.emplace_back(Mask, S); + } + } +}; + 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); + std::unique_ptr SCL = + RustSanitizerSpecialCaseList::create( + PathsVec, *llvm::vfs::getRealFileSystem(), Error); if (!SCL) { LLVMRustStringWriteImpl(ErrorMsg, Error.data(), Error.size()); return nullptr; @@ -1845,7 +1946,7 @@ LLVMRustSpecialCaseListCreate(const char **Paths, size_t NumPaths, } extern "C" void LLVMRustSpecialCaseListDestroy(LLVMSpecialCaseListRef List) { - delete reinterpret_cast(List); + delete reinterpret_cast(List); } struct LLVMRustSpecialCaseListBlame { @@ -1855,18 +1956,20 @@ struct LLVMRustSpecialCaseListBlame { extern "C" void LLVMRustSpecialCaseListInSectionBlame(LLVMSpecialCaseListRef List, + uint32_t Mask, const char *Section, const char *Prefix, const char *Query, LLVMRustSpecialCaseListBlame *OutNoSan, LLVMRustSpecialCaseListBlame *OutSan) { - auto *SCL = reinterpret_cast(List); + auto *SSCL = reinterpret_cast(List); + llvm::StringRef SectionStr = Section ? Section : ""; std::pair NoSan = - SCL->inSectionBlame(Section, Prefix, Query); + SSCL->inSectionBlame(Mask, SectionStr, Prefix, Query); OutNoSan->FileIdx = NoSan.first; OutNoSan->LineNo = NoSan.second; std::pair San = - SCL->inSectionBlame(Section, Prefix, Query, "sanitize"); + SSCL->inSectionBlame(Mask, SectionStr, Prefix, Query, "sanitize"); OutSan->FileIdx = San.first; OutSan->LineNo = San.second; } diff --git a/compiler/rustc_sanitizers/src/ignorelist/ffi.rs b/compiler/rustc_sanitizers/src/ignorelist/ffi.rs index abf86b28dd1c3..8f8b14de6db1d 100644 --- a/compiler/rustc_sanitizers/src/ignorelist/ffi.rs +++ b/compiler/rustc_sanitizers/src/ignorelist/ffi.rs @@ -42,6 +42,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustSpecialCaseListDestroy(List: *mut Opaque); pub(crate) fn LLVMRustSpecialCaseListInSectionBlame( List: *const Opaque, + Mask: u32, Section: *const c_char, Prefix: *const c_char, Query: *const c_char, diff --git a/compiler/rustc_sanitizers/src/ignorelist/mod.rs b/compiler/rustc_sanitizers/src/ignorelist/mod.rs index edb01da24037a..6303e1f5d168d 100644 --- a/compiler/rustc_sanitizers/src/ignorelist/mod.rs +++ b/compiler/rustc_sanitizers/src/ignorelist/mod.rs @@ -1,8 +1,7 @@ +pub use ffi::Blame; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_target::spec::SanitizerSet; -pub use ffi::Blame; - pub(crate) mod ffi; #[inline] @@ -37,12 +36,38 @@ impl SanitizerIgnoreList { if inner.is_null() { Err(err) } else { Ok(Self { inner }) } } + pub fn in_sanitizer_blame( + &self, + sanitizer: SanitizerSet, + prefix: &std::ffi::CStr, + query: &str, + ) -> (Blame, Blame) { + let mut no_san = Blame::NONE; + let mut san = Blame::NONE; + let Ok(query) = std::ffi::CString::new(query) else { + return (Blame::NONE, Blame::NONE); + }; + unsafe { + ffi::LLVMRustSpecialCaseListInSectionBlame( + self.inner, + sanitizer.bits() as u32, + std::ptr::null(), + prefix.as_ptr(), + query.as_ptr(), + &mut no_san, + &mut san, + ); + } + (no_san, san) + } + pub fn in_section_blame( &self, section: &std::ffi::CStr, prefix: &std::ffi::CStr, query: &str, ) -> (Blame, Blame) { + let mask = section_to_sanitizer_set(section); let mut no_san = Blame::NONE; let mut san = Blame::NONE; let Ok(query) = std::ffi::CString::new(query) else { @@ -51,6 +76,7 @@ impl SanitizerIgnoreList { unsafe { ffi::LLVMRustSpecialCaseListInSectionBlame( self.inner, + mask.map(|s| s.bits() as u32).unwrap_or(0), section.as_ptr(), prefix.as_ptr(), query.as_ptr(), @@ -116,13 +142,12 @@ impl SanitizerIgnoreList { let (address_nosan, address_san) = self.instance_blame(tcx, instance, c"address"); let (kaddress_nosan, kaddress_san) = self.instance_blame(tcx, instance, c"kernel-address"); let (hwaddress_nosan, hwaddress_san) = self.instance_blame(tcx, instance, c"hwaddress"); - let (khwaddress_nosan, khwaddress_san) = self.instance_blame(tcx, instance, c"kernel-hwaddress"); + let (khwaddress_nosan, khwaddress_san) = + self.instance_blame(tcx, instance, c"kernel-hwaddress"); let ignore_address = is_blame_ignored(address_nosan, address_san); - let ignore_kernel_address = is_blame_ignored( - address_nosan.max(kaddress_nosan), - address_san.max(kaddress_san), - ); + let ignore_kernel_address = + is_blame_ignored(address_nosan.max(kaddress_nosan), address_san.max(kaddress_san)); let ignore_hwaddress = is_blame_ignored(hwaddress_nosan, hwaddress_san); let ignore_kernel_hwaddress = is_blame_ignored( @@ -136,10 +161,14 @@ impl SanitizerIgnoreList { if enabled.contains(SanitizerSet::KERNELADDRESS) && ignore_kernel_address { enabled.remove(SanitizerSet::KERNELADDRESS); } - if enabled.contains(SanitizerSet::MEMORY) && self.is_instance_ignored(tcx, instance, c"memory") { + if enabled.contains(SanitizerSet::MEMORY) + && self.is_instance_ignored(tcx, instance, c"memory") + { enabled.remove(SanitizerSet::MEMORY); } - if enabled.contains(SanitizerSet::THREAD) && self.is_instance_ignored(tcx, instance, c"thread") { + if enabled.contains(SanitizerSet::THREAD) + && self.is_instance_ignored(tcx, instance, c"thread") + { enabled.remove(SanitizerSet::THREAD); } if enabled.contains(SanitizerSet::HWADDRESS) && ignore_hwaddress { @@ -148,7 +177,9 @@ impl SanitizerIgnoreList { if enabled.contains(SanitizerSet::KERNELHWADDRESS) && ignore_kernel_hwaddress { enabled.remove(SanitizerSet::KERNELHWADDRESS); } - if enabled.contains(SanitizerSet::SAFESTACK) && self.is_instance_ignored(tcx, instance, c"safestack") { + if enabled.contains(SanitizerSet::SAFESTACK) + && self.is_instance_ignored(tcx, instance, c"safestack") + { enabled.remove(SanitizerSet::SAFESTACK); } @@ -190,3 +221,23 @@ pub fn type_name_for_ignore_list<'tcx>( let fn_ptr = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig)); ty::print::with_no_trimmed_paths!(fn_ptr.to_string()) } + +fn section_to_sanitizer_set(section: &std::ffi::CStr) -> Option { + match section.to_bytes() { + b"address" => Some(SanitizerSet::ADDRESS), + b"kernel-address" | b"kasan" => Some(SanitizerSet::KERNELADDRESS), + b"memory" => Some(SanitizerSet::MEMORY), + b"thread" => Some(SanitizerSet::THREAD), + b"hwaddress" => Some(SanitizerSet::HWADDRESS), + b"kernel-hwaddress" | b"khwasan" => Some(SanitizerSet::KERNELHWADDRESS), + b"safestack" | b"safe-stack" => Some(SanitizerSet::SAFESTACK), + b"shadow-call-stack" | b"shadowcallstack" => Some(SanitizerSet::SHADOWCALLSTACK), + b"cfi" | b"cfi-icall" => Some(SanitizerSet::CFI), + b"kcfi" => Some(SanitizerSet::KCFI), + b"memtag" => Some(SanitizerSet::MEMTAG), + b"realtime" => Some(SanitizerSet::REALTIME), + b"leak" => Some(SanitizerSet::LEAK), + b"dataflow" => Some(SanitizerSet::DATAFLOW), + _ => None, + } +} From 1cb8361a0efd8aa3ec2f0e6a1e7a423e28c23cd4 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Thu, 27 Aug 2026 16:21:21 +0000 Subject: [PATCH 9/9] Reformat C++ code --- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 73 +++++++++++++------ 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 9da80fcdc43ad..2aaf53b930a09 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1851,13 +1851,13 @@ class RustSanitizerSpecialCaseList : public llvm::SpecialCaseList { bool Matches = false; if (Mask != 0 && (It->Mask & Mask) != 0) { Matches = true; - } else if (!SectionName.empty() && It->S.matchName(SectionName)) { + } else if (!SectionName.empty() && matchSection(It->S, SectionName)) { Matches = true; } if (Matches) { - unsigned LineNum = It->S.getLastMatch(Prefix, Query, Category); + unsigned LineNum = getLastMatch(It->S, Prefix, Query, Category); if (LineNum > 0) - return {It->S.fileIndex(), LineNum}; + return {getFileIndex(It->S), LineNum}; } } return NotFound; @@ -1872,57 +1872,86 @@ class RustSanitizerSpecialCaseList : public llvm::SpecialCaseList { std::vector SanitizerSections; +#if LLVM_VERSION_GE(22, 0) + static bool matchSection(const Section &S, llvm::StringRef Name) { + return S.matchName(Name); + } + unsigned getLastMatch(const Section &S, llvm::StringRef Prefix, llvm::StringRef Query, + llvm::StringRef Category) const { + return S.getLastMatch(Prefix, Query, Category); + } + static unsigned getFileIndex(const Section &S) { + return S.fileIndex(); + } +#else + static bool matchSection(const Section &S, llvm::StringRef Name) { + return S.SectionMatcher && S.SectionMatcher->match(Name) != 0; + } + unsigned getLastMatch(const Section &S, llvm::StringRef Prefix, llvm::StringRef Query, + llvm::StringRef Category) const { + return llvm::SpecialCaseList::inSectionBlame(S.Entries, Prefix, Query, Category); + } + static unsigned getFileIndex(const Section &S) { + return S.FileIdx; + } +#endif + void createSanitizerSections() { - for (const auto &S : sections()) { +#if LLVM_VERSION_GE(22, 0) + const auto &SecList = sections(); +#else + const auto &SecList = Sections; +#endif + for (const auto &S : SecList) { uint32_t Mask = 0; // Address: [address] - if (S.matchName("address")) + if (matchSection(S, "address")) Mask |= (1 << 0); // Leak: [leak] - if (S.matchName("leak")) + if (matchSection(S, "leak")) Mask |= (1 << 1); // Memory: [memory] - if (S.matchName("memory")) + if (matchSection(S, "memory")) Mask |= (1 << 2); // Thread: [thread] - if (S.matchName("thread")) + if (matchSection(S, "thread")) Mask |= (1 << 3); // HWAddress: [hwaddress] - if (S.matchName("hwaddress")) + if (matchSection(S, "hwaddress")) Mask |= (1 << 4); // CFI and its sub-kinds: [cfi], [cfi-icall], [cfi-vcall], [{cfi-vcall,cfi-icall}], etc. - if (S.matchName("cfi") || S.matchName("cfi-icall") || - S.matchName("cfi-vcall") || S.matchName("cfi-nvcall") || - S.matchName("cfi-derived-cast") || S.matchName("cfi-unrelated-cast") || - S.matchName("cfi-mfcall")) + if (matchSection(S, "cfi") || matchSection(S, "cfi-icall") || + matchSection(S, "cfi-vcall") || matchSection(S, "cfi-nvcall") || + matchSection(S, "cfi-derived-cast") || matchSection(S, "cfi-unrelated-cast") || + matchSection(S, "cfi-mfcall")) Mask |= (1 << 5); // MemTag: [memtag], [memtag-stack], [memtag-heap], [memtag-globals] - if (S.matchName("memtag") || S.matchName("memtag-stack") || - S.matchName("memtag-heap") || S.matchName("memtag-globals")) + if (matchSection(S, "memtag") || matchSection(S, "memtag-stack") || + matchSection(S, "memtag-heap") || matchSection(S, "memtag-globals")) Mask |= (1 << 6); // ShadowCallStack: [shadow-call-stack], [shadowcallstack] - if (S.matchName("shadow-call-stack") || S.matchName("shadowcallstack")) + if (matchSection(S, "shadow-call-stack") || matchSection(S, "shadowcallstack")) Mask |= (1 << 7); // KCFI: [kcfi] - if (S.matchName("kcfi")) + if (matchSection(S, "kcfi")) Mask |= (1 << 8); // KernelAddress: [kernel-address], [kasan] - if (S.matchName("kernel-address") || S.matchName("kasan")) + if (matchSection(S, "kernel-address") || matchSection(S, "kasan")) Mask |= (1 << 9); // KernelHWAddress: [kernel-hwaddress], [khwasan] - if (S.matchName("kernel-hwaddress") || S.matchName("khwasan")) + if (matchSection(S, "kernel-hwaddress") || matchSection(S, "khwasan")) Mask |= (1 << 10); // SafeStack: [safe-stack] (Clang standard), [safestack] - if (S.matchName("safe-stack") || S.matchName("safestack")) + if (matchSection(S, "safe-stack") || matchSection(S, "safestack")) Mask |= (1 << 11); // DataFlow: [dataflow] - if (S.matchName("dataflow")) + if (matchSection(S, "dataflow")) Mask |= (1 << 12); // Realtime: [realtime] - if (S.matchName("realtime")) + if (matchSection(S, "realtime")) Mask |= (1 << 13); SanitizerSections.emplace_back(Mask, S);