Skip to main content

rustc_sanitizers/ignorelist/
mod.rs

1pub use ffi::Blame;
2use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
3use rustc_target::spec::SanitizerSet;
4
5pub(crate) mod ffi;
6
7#[inline]
8pub fn is_blame_ignored(no_san: Blame, san: Blame) -> bool {
9    no_san.is_some() && (san.is_none() || no_san > san)
10}
11
12pub struct SanitizerIgnoreList {
13    inner: *mut ffi::Opaque,
14}
15
16#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for InstanceSanitizers { }
#[automatically_derived]
impl ::core::clone::Clone for InstanceSanitizers {
    #[inline]
    fn clone(&self) -> InstanceSanitizers {
        let _: ::core::clone::AssertParamIsClone<SanitizerSet>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for InstanceSanitizers { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for InstanceSanitizers {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "InstanceSanitizers", "enabled", &self.enabled, "ignore_cfi",
            &self.ignore_cfi, "ignore_kcfi", &&self.ignore_kcfi)
    }
}Debug)]
17pub struct InstanceSanitizers {
18    pub enabled: SanitizerSet,
19    pub ignore_cfi: bool,
20    pub ignore_kcfi: bool,
21}
22
23impl SanitizerIgnoreList {
24    pub fn new(paths: &[String]) -> Result<Self, String> {
25        use std::ffi::CString;
26        let c_paths: Vec<CString> =
27            paths.iter().map(|p| CString::new(p.as_str()).unwrap()).collect();
28        let c_ptrs: Vec<*const libc::c_char> = c_paths.iter().map(|c| c.as_ptr()).collect();
29
30        let mut inner = std::ptr::null_mut();
31        let err = ffi::build_string(|err| unsafe {
32            inner = ffi::LLVMRustSpecialCaseListCreate(c_ptrs.as_ptr(), c_ptrs.len(), err);
33        });
34
35        let err = err.unwrap_or_else(|e| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("utf8 error: {0}", e))
    })format!("utf8 error: {}", e));
36        if inner.is_null() { Err(err) } else { Ok(Self { inner }) }
37    }
38
39    pub fn in_section_blame(
40        &self,
41        section: &std::ffi::CStr,
42        prefix: &std::ffi::CStr,
43        query: &str,
44    ) -> (Blame, Blame) {
45        let mask = section_to_sanitizer_set(section);
46        let mut no_san = Blame::NONE;
47        let mut san = Blame::NONE;
48        let Ok(query) = std::ffi::CString::new(query) else {
49            return (Blame::NONE, Blame::NONE);
50        };
51        unsafe {
52            ffi::LLVMRustSpecialCaseListInSectionBlame(
53                self.inner,
54                mask.map(|s| s.bits() as u32).unwrap_or(0),
55                section.as_ptr(),
56                prefix.as_ptr(),
57                query.as_ptr(),
58                &mut no_san,
59                &mut san,
60            );
61        }
62        (no_san, san)
63    }
64
65    pub fn instance_blame<'tcx>(
66        &self,
67        tcx: TyCtxt<'tcx>,
68        instance: Instance<'tcx>,
69        section: &std::ffi::CStr,
70    ) -> (Blame, Blame) {
71        let sym_name = tcx.symbol_name(instance).name;
72        let span = tcx.def_span(instance.def_id());
73        let filename =
74            tcx.sess.source_map().span_to_filename(span).prefer_local_unconditionally().to_string();
75        let mainfile = tcx
76            .sess
77            .local_crate_source_file()
78            .and_then(|path| path.local_path().map(|p| p.display().to_string()))
79            .unwrap_or_default();
80        let demangled =
81            { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(instance.def_id()) }rustc_middle::ty::print::with_no_trimmed_paths!(tcx.def_path_str(instance.def_id()));
82
83        let mut no_san = Blame::NONE;
84        let mut san = Blame::NONE;
85        let mut update = |prefix: &std::ffi::CStr, query: &str| {
86            let (ns, s) = self.in_section_blame(section, prefix, query);
87            no_san = no_san.max(ns);
88            san = san.max(s);
89        };
90
91        update(c"fun", sym_name);
92        update(c"fun", &demangled);
93        update(c"src", &filename);
94        if !mainfile.is_empty() {
95            update(c"mainfile", &mainfile);
96        }
97
98        (no_san, san)
99    }
100
101    pub fn is_instance_ignored<'tcx>(
102        &self,
103        tcx: TyCtxt<'tcx>,
104        instance: Instance<'tcx>,
105        section: &std::ffi::CStr,
106    ) -> bool {
107        let (no_san, san) = self.instance_blame(tcx, instance, section);
108        is_blame_ignored(no_san, san)
109    }
110
111    pub fn filter_instance_sanitizers<'tcx>(
112        &self,
113        tcx: TyCtxt<'tcx>,
114        instance: Instance<'tcx>,
115        mut enabled: SanitizerSet,
116    ) -> InstanceSanitizers {
117        let (address_nosan, address_san) = self.instance_blame(tcx, instance, c"address");
118        let (kaddress_nosan, kaddress_san) = self.instance_blame(tcx, instance, c"kernel-address");
119        let (hwaddress_nosan, hwaddress_san) = self.instance_blame(tcx, instance, c"hwaddress");
120        let (khwaddress_nosan, khwaddress_san) =
121            self.instance_blame(tcx, instance, c"kernel-hwaddress");
122
123        let ignore_address = is_blame_ignored(address_nosan, address_san);
124        let ignore_kernel_address = is_blame_ignored(kaddress_nosan, kaddress_san);
125
126        let ignore_hwaddress = is_blame_ignored(hwaddress_nosan, hwaddress_san);
127        let ignore_kernel_hwaddress = is_blame_ignored(khwaddress_nosan, khwaddress_san);
128
129        if enabled.contains(SanitizerSet::ADDRESS) && ignore_address {
130            enabled.remove(SanitizerSet::ADDRESS);
131        }
132        if enabled.contains(SanitizerSet::KERNELADDRESS) && ignore_kernel_address {
133            enabled.remove(SanitizerSet::KERNELADDRESS);
134        }
135        if enabled.contains(SanitizerSet::MEMORY)
136            && self.is_instance_ignored(tcx, instance, c"memory")
137        {
138            enabled.remove(SanitizerSet::MEMORY);
139        }
140        if enabled.contains(SanitizerSet::THREAD)
141            && self.is_instance_ignored(tcx, instance, c"thread")
142        {
143            enabled.remove(SanitizerSet::THREAD);
144        }
145        if enabled.contains(SanitizerSet::HWADDRESS) && ignore_hwaddress {
146            enabled.remove(SanitizerSet::HWADDRESS);
147        }
148        if enabled.contains(SanitizerSet::KERNELHWADDRESS) && ignore_kernel_hwaddress {
149            enabled.remove(SanitizerSet::KERNELHWADDRESS);
150        }
151        // FIXME: Add support for filtering SAFESTACK, SHADOWCALLSTACK, MEMTAG, and REALTIME.
152        // Note: For REALTIME, `rustc_codegen_llvm::attributes::sanitize_attrs` will also
153        // need to check the filtered `enabled` set rather than `tcx.sess.sanitizers()`.
154
155        let ignore_cfi = self.is_instance_ignored(tcx, instance, c"cfi");
156        let ignore_kcfi = self.is_instance_ignored(tcx, instance, c"kcfi");
157
158        InstanceSanitizers { enabled, ignore_cfi, ignore_kcfi }
159    }
160
161    pub fn contains_prefix(
162        &self,
163        section: &std::ffi::CStr,
164        prefix: &std::ffi::CStr,
165        query: &str,
166    ) -> bool {
167        let (no_san, san) = self.in_section_blame(section, prefix, query);
168        is_blame_ignored(no_san, san)
169    }
170}
171
172impl Drop for SanitizerIgnoreList {
173    fn drop(&mut self) {
174        unsafe {
175            ffi::LLVMRustSpecialCaseListDestroy(self.inner);
176        }
177    }
178}
179
180pub fn typename_for_ignore_list<'tcx>(
181    tcx: TyCtxt<'tcx>,
182    fn_abi: &rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>,
183) -> String {
184    let inputs: Vec<_> = fn_abi.args.iter().map(|arg| arg.layout.ty).collect();
185    let output = fn_abi.ret.layout.ty;
186    let mut fn_sig_kind = ty::FnSigKind::default();
187    fn_sig_kind = fn_sig_kind.set_safety(rustc_hir::Safety::Safe);
188    fn_sig_kind = fn_sig_kind.set_c_variadic(fn_abi.c_variadic);
189    let fn_sig = tcx.mk_fn_sig(inputs, output, fn_sig_kind);
190    let fn_ptr = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
191    { let _guard = NoTrimmedGuard::new(); fn_ptr.to_string() }ty::print::with_no_trimmed_paths!(fn_ptr.to_string())
192}
193
194fn section_to_sanitizer_set(section: &std::ffi::CStr) -> Option<SanitizerSet> {
195    match section.to_bytes() {
196        b"address" => Some(SanitizerSet::ADDRESS),
197        b"kernel-address" | b"kasan" => Some(SanitizerSet::KERNELADDRESS),
198        b"memory" => Some(SanitizerSet::MEMORY),
199        b"thread" => Some(SanitizerSet::THREAD),
200        b"hwaddress" => Some(SanitizerSet::HWADDRESS),
201        b"kernel-hwaddress" | b"khwasan" => Some(SanitizerSet::KERNELHWADDRESS),
202        b"safestack" | b"safe-stack" => Some(SanitizerSet::SAFESTACK),
203        b"shadow-call-stack" | b"shadowcallstack" => Some(SanitizerSet::SHADOWCALLSTACK),
204        b"cfi" | b"cfi-icall" => Some(SanitizerSet::CFI),
205        b"kcfi" => Some(SanitizerSet::KCFI),
206        b"memtag" => Some(SanitizerSet::MEMTAG),
207        b"realtime" => Some(SanitizerSet::REALTIME),
208        b"leak" => Some(SanitizerSet::LEAK),
209        b"dataflow" => Some(SanitizerSet::DATAFLOW),
210        _ => None,
211    }
212}