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/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..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, @@ -137,9 +138,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 +477,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( llfn: &'ll Value, codegen_fn_attrs: &CodegenFnAttrs, instance: Option>, + sanitizer_ignorelist: Option<&SanitizerIgnoreList>, ) { let sess = tcx.sess; let mut to_add = SmallVec::<[_; 16]>::new(); @@ -537,7 +539,20 @@ 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 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 result.ignore_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..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()); + 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..dae8b2d17e0e1 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,6 +2021,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } + if self.cx.is_sanitizer_type_ignored(c"cfi", fn_abi) { + return; + } + let typeid = if let Some(instance) = instance { cfi::typeid_for_instance(self.tcx, instance, options) } else { @@ -2123,6 +2130,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { { return None; } + if crate::llvm::HasStringAttribute(self.llfn(), "no-sanitize-kcfi") { + return None; + } let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { @@ -2132,6 +2142,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } + if self.cx.is_sanitizer_type_ignored(c"kcfi", fn_abi) { + return None; + } + let kcfi_typeid = if let Some(instance) = instance { kcfi::typeid_for_instance(self.tcx, instance, options) } else { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 24e52743fbe30..3053125b026f7 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -570,6 +570,81 @@ 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 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 (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) + && 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..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,6 +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, eh_personality: Cell>, pub rust_try_fn: Cell>, @@ -680,6 +682,24 @@ 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 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 +719,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(), @@ -838,6 +859,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 = type_name_for_ignore_list(self.tcx, fn_abi); + ignorelist.contains_prefix(sanitizer, c"type", &type_name) + }) + } } impl<'ll> SimpleCx<'ll> { pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 419d38f95e595..3511056f79306 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -232,13 +232,12 @@ 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); + let kcfi_typeid = if let Some(instance) = instance { + kcfi::typeid_for_instance(self.tcx, instance, options) } else { - let kcfi_typeid = kcfi::typeid_for_fnabi(self.tcx, fn_abi, options); - self.set_kcfi_type_metadata(llfn, kcfi_typeid); - } + kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) + }; + self.set_kcfi_type_metadata(llfn, kcfi_typeid); } llfn diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8c9bf55b14e45..453053b197dda 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> { 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..58ab0e060cc55 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,177 @@ 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 + +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() && matchSection(It->S, SectionName)) { + Matches = true; + } + if (Matches) { + unsigned LineNum = getLastMatch(It->S, Prefix, Query, Category); + if (LineNum > 0) + return {getFileIndex(It->S), 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; + +#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() { +#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 (matchSection(S, "address")) + Mask |= (1 << 0); + // Leak: [leak] + if (matchSection(S, "leak")) + Mask |= (1 << 1); + // Memory: [memory] + if (matchSection(S, "memory")) + Mask |= (1 << 2); + // Thread: [thread] + if (matchSection(S, "thread")) + Mask |= (1 << 3); + // HWAddress: [hwaddress] + if (matchSection(S, "hwaddress")) + Mask |= (1 << 4); + + // CFI and its sub-kinds: [cfi], [cfi-icall], [cfi-vcall], + // [{cfi-vcall,cfi-icall}], etc. + 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 (matchSection(S, "memtag") || matchSection(S, "memtag-stack") || + matchSection(S, "memtag-heap") || matchSection(S, "memtag-globals")) + Mask |= (1 << 6); + // ShadowCallStack: [shadow-call-stack], [shadowcallstack] + if (matchSection(S, "shadow-call-stack") || + matchSection(S, "shadowcallstack")) + Mask |= (1 << 7); + // KCFI: [kcfi] + if (matchSection(S, "kcfi")) + Mask |= (1 << 8); + // KernelAddress: [kernel-address], [kasan] + if (matchSection(S, "kernel-address") || matchSection(S, "kasan")) + Mask |= (1 << 9); + // KernelHWAddress: [kernel-hwaddress], [khwasan] + if (matchSection(S, "kernel-hwaddress") || matchSection(S, "khwasan")) + Mask |= (1 << 10); + // SafeStack: [safe-stack] (Clang standard), [safestack] + if (matchSection(S, "safe-stack") || matchSection(S, "safestack")) + Mask |= (1 << 11); + // DataFlow: [dataflow] + if (matchSection(S, "dataflow")) + Mask |= (1 << 12); + // Realtime: [realtime] + if (matchSection(S, "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 = + RustSanitizerSpecialCaseList::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); +} + +struct LLVMRustSpecialCaseListBlame { + uint32_t FileIdx; + uint32_t LineNo; +}; + +extern "C" void +LLVMRustSpecialCaseListInSectionBlame(LLVMSpecialCaseListRef List, + uint32_t Mask, const char *Section, + const char *Prefix, const char *Query, + LLVMRustSpecialCaseListBlame *OutNoSan, + LLVMRustSpecialCaseListBlame *OutSan) { + auto *SSCL = reinterpret_cast(List); + llvm::StringRef SectionStr = Section ? Section : ""; + std::pair NoSan = + SSCL->inSectionBlame(Mask, SectionStr, Prefix, Query); + OutNoSan->FileIdx = NoSan.first; + OutNoSan->LineNo = NoSan.second; + + std::pair San = + SSCL->inSectionBlame(Mask, SectionStr, Prefix, Query, "sanitize"); + OutSan->FileIdx = San.first; + OutSan->LineNo = San.second; +} 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..8f8b14de6db1d --- /dev/null +++ b/compiler/rustc_sanitizers/src/ignorelist/ffi.rs @@ -0,0 +1,88 @@ +use std::cell::RefCell; +use std::ffi::c_char; +use std::ptr; +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, + /// 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 LLVMRustSpecialCaseListInSectionBlame( + List: *const Opaque, + Mask: u32, + Section: *const c_char, + Prefix: *const c_char, + Query: *const c_char, + OutNoSan: *mut Blame, + OutSan: *mut Blame, + ); +} + +/// 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..6303e1f5d168d --- /dev/null +++ b/compiler/rustc_sanitizers/src/ignorelist/mod.rs @@ -0,0 +1,243 @@ +pub use ffi::Blame; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_target::spec::SanitizerSet; + +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, +} + +#[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 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 { + return (Blame::NONE, Blame::NONE); + }; + unsafe { + ffi::LLVMRustSpecialCaseListInSectionBlame( + self.inner, + mask.map(|s| s.bits() as u32).unwrap_or(0), + 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, + ) -> (Blame, Blame) { + 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())); + + 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>( + &self, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + mut enabled: SanitizerSet, + ) -> InstanceSanitizers { + 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); + } + 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") + { + enabled.remove(SanitizerSet::MEMORY); + } + if enabled.contains(SanitizerSet::THREAD) + && self.is_instance_ignored(tcx, instance, 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) + && self.is_instance_ignored(tcx, instance, c"safestack") + { + enabled.remove(SanitizerSet::SAFESTACK); + } + + 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 } + } + + pub fn contains_prefix( + &self, + section: &std::ffi::CStr, + prefix: &std::ffi::CStr, + query: &str, + ) -> bool { + let (no_san, san) = self.in_section_blame(section, prefix, query); + is_blame_ignored(no_san, san) + } +} + +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()) +} + +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, + } +} 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/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/ignorelist/cfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs new file mode 100644 index 0000000000000..a8a64878499bb --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/cfi-ignorelist.rs @@ -0,0 +1,23 @@ +//@ needs-sanitizer-cfi +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_cfi +// CHECK-SAME: !type +// CHECK-NOT: trap +// CHECK: call void %f() +#[no_mangle] +pub fn test_cfi(f: fn(), x: &mut i32) { + *x = 1; + 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; + f(1); +} diff --git a/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.rs new file mode 100644 index 0000000000000..a59735921e9e1 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.rs @@ -0,0 +1,13 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/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/ignorelist/global-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.txt new file mode 100644 index 0000000000000..b82472fb39422 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/global-ignorelist.txt @@ -0,0 +1,3 @@ +[address] +global:*IGNORED_GLOBAL* +src:*global-ignorelist.rs diff --git a/tests/codegen-llvm/sanitizer/ignorelist/ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/ignorelist.rs new file mode 100644 index 0000000000000..a589d24134c11 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/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/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/ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/ignorelist.txt new file mode 100644 index 0000000000000..16c9b8b284714 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/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/ignorelist/kcfi-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs new file mode 100644 index 0000000000000..72eef9d21513f --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/kcfi-ignorelist.rs @@ -0,0 +1,23 @@ +//@ needs-sanitizer-kcfi +//@ compile-flags: -Zsanitizer=kcfi -C panic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_kcfi +// CHECK-SAME: !kcfi_type +// CHECK-NOT: [ "kcfi" +// CHECK: call void %f() +#[no_mangle] +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(f: fn(i32), x: &mut i32) { + *x = 2; + f(1); +} diff --git a/tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.rs new file mode 100644 index 0000000000000..293a261daa0e8 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.rs @@ -0,0 +1,20 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/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/ignorelist/kernel-address-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/kernel-address-ignorelist.txt new file mode 100644 index 0000000000000..c21e081a1057f --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/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/ignorelist/mainfile-ignore.rs b/tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignore.rs new file mode 100644 index 0000000000000..b59ff94e054ed --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignore.rs @@ -0,0 +1,12 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/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/ignorelist/mainfile-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignorelist.txt new file mode 100644 index 0000000000000..e446cec256466 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/mainfile-ignorelist.txt @@ -0,0 +1,2 @@ +[address] +mainfile:*mainfile-ignore.rs diff --git a/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs new file mode 100644 index 0000000000000..63c9f9bbebf53 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.rs @@ -0,0 +1,30 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/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; +} + +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 new file mode 100644 index 0000000000000..5bcf5a1e5c398 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/override-ignorelist.txt @@ -0,0 +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 diff --git a/tests/codegen-llvm/sanitizer/ignorelist/src-ignore-memory.rs b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore-memory.rs new file mode 100644 index 0000000000000..8c533a6cc53ab --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/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/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/ignorelist/src-ignore.rs b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs new file mode 100644 index 0000000000000..0c57340326997 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/src-ignore.rs @@ -0,0 +1,15 @@ +//@ needs-sanitizer-cfi +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt + +#![crate_type = "lib"] + +// CHECK: define void @test_file +// 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; + f(); +} diff --git a/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-asan.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-asan.rs new file mode 100644 index 0000000000000..65c441f9861d6 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-asan.rs @@ -0,0 +1,21 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/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/ignorelist/type-ignorelist-kcfi.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs new file mode 100644 index 0000000000000..5546caba1772f --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist-kcfi.rs @@ -0,0 +1,21 @@ +//@ needs-sanitizer-kcfi +//@ compile-flags: -Zsanitizer=kcfi -Cpanic=abort -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt + +#![crate_type = "lib"] + +// 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(), g: fn(i32), x: &mut i32) { + *x = 1; + f(); + g(1); +} + +// CHECK: define void @test_type_2() +// 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 new file mode 100644 index 0000000000000..20904138a29bc --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-ignorelist.rs @@ -0,0 +1,21 @@ +//@ needs-sanitizer-cfi +//@ compile-flags: -Zsanitizer=cfi -Clto -Cunsafe-allow-abi-mismatch=sanitizer -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/ignorelist.txt + +#![crate_type = "lib"] + +// 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(), g: fn(i32), x: &mut i32) { + *x = 1; + f(); + g(1); +} + +// CHECK: define void @test_type_2() +// CHECK-SAME: !type +#[no_mangle] +pub fn test_type_2() {} diff --git a/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe-ignorelist.txt b/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe-ignorelist.txt new file mode 100644 index 0000000000000..df3db6b38ceb0 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe-ignorelist.txt @@ -0,0 +1,2 @@ +[address] +type:*unsafe*extern*fn* diff --git a/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe.rs b/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe.rs new file mode 100644 index 0000000000000..f7f9d7538f78c --- /dev/null +++ b/tests/codegen-llvm/sanitizer/ignorelist/type-string-unsafe.rs @@ -0,0 +1,10 @@ +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Zsanitizer-ignorelist={{src-base}}/sanitizer/ignorelist/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() {}