about summary refs log tree commit diff
path: root/compiler/rustc_passes/src
diff options
context:
space:
mode:
authorBastian Kersting <bkersting@google.com>2025-06-18 12:53:34 +0000
committerBastian Kersting <bkersting@google.com>2025-08-18 08:30:00 +0000
commit3ef065bf872ce62a18336dca0daf47b3e9f7da64 (patch)
treeb4dccd0b519642cac3b2f057995f5b378c8abc01 /compiler/rustc_passes/src
parent425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0 (diff)
downloadrust-3ef065bf872ce62a18336dca0daf47b3e9f7da64.tar.gz
rust-3ef065bf872ce62a18336dca0daf47b3e9f7da64.zip
Implement the #[sanitize(..)] attribute
This change implements the #[sanitize(..)] attribute, which opts to
replace the currently unstable #[no_sanitize]. Essentially the new
attribute works similar as #[no_sanitize], just with more flexible
options regarding where it is applied. E.g. it is possible to turn
a certain sanitizer either on or off:
`#[sanitize(address = "on|off")]`

This attribute now also applies to more places, e.g. it is possible
to turn off a sanitizer for an entire module or impl block:
```rust
\#[sanitize(address = "off")]
mod foo {
    fn unsanitized(..) {}

    #[sanitize(address = "on")]
    fn sanitized(..) {}
}

\#[sanitize(thread = "off")]
impl MyTrait for () {
    ...
}
```

This attribute is enabled behind the unstable `sanitize` feature.
Diffstat (limited to 'compiler/rustc_passes/src')
-rw-r--r--compiler/rustc_passes/src/check_attr.rs44
-rw-r--r--compiler/rustc_passes/src/errors.rs17
2 files changed, 60 insertions, 1 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 0e28c51e981..e51f3657eaa 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -261,6 +261,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::no_sanitize, ..] => {
                             self.check_no_sanitize(attr, span, target)
                         }
+                        [sym::sanitize, ..] => {
+                            self.check_sanitize(attr, span, target)
+                        }
                         [sym::thread_local, ..] => self.check_thread_local(attr, span, target),
                         [sym::doc, ..] => self.check_doc_attrs(
                             attr,
@@ -518,6 +521,46 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
+    /// Checks that the `#[sanitize(..)]` attribute is applied to a
+    /// function/closure/method, or to an impl block or module.
+    fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) {
+        let mut not_fn_impl_mod = None;
+        let mut no_body = None;
+
+        if let Some(list) = attr.meta_item_list() {
+            for item in list.iter() {
+                let MetaItemInner::MetaItem(set) = item else {
+                    return;
+                };
+                let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
+                match target {
+                    Target::Fn
+                    | Target::Closure
+                    | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
+                    | Target::Impl { .. }
+                    | Target::Mod => return,
+                    Target::Static if matches!(segments.as_slice(), [sym::address]) => return,
+
+                    // These are "functions", but they aren't allowed because they don't
+                    // have a body, so the usual explanation would be confusing.
+                    Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
+                        no_body = Some(target_span);
+                    }
+
+                    _ => {
+                        not_fn_impl_mod = Some(target_span);
+                    }
+                }
+            }
+            self.dcx().emit_err(errors::SanitizeAttributeNotAllowed {
+                attr_span: attr.span(),
+                not_fn_impl_mod,
+                no_body,
+                help: (),
+            });
+        }
+    }
+
     /// Checks if `#[naked]` is applied to a function definition.
     fn check_naked(&self, hir_id: HirId, target: Target) {
         match target {
@@ -561,7 +604,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }
         }
     }
-
     /// Checks if `#[collapse_debuginfo]` is applied to a macro.
     fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) {
         match target {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index c5d5155d0e5..bd608dc2fee 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1499,6 +1499,23 @@ pub(crate) struct NoSanitize<'a> {
     pub attr_str: &'a str,
 }
 
+/// "sanitize attribute not allowed here"
+#[derive(Diagnostic)]
+#[diag(passes_sanitize_attribute_not_allowed)]
+pub(crate) struct SanitizeAttributeNotAllowed {
+    #[primary_span]
+    pub attr_span: Span,
+    /// "not a function, impl block, or module"
+    #[label(passes_not_fn_impl_mod)]
+    pub not_fn_impl_mod: Option<Span>,
+    /// "function has no body"
+    #[label(passes_no_body)]
+    pub no_body: Option<Span>,
+    /// "sanitize attribute can be applied to a function (with body), impl block, or module"
+    #[help]
+    pub help: (),
+}
+
 // FIXME(jdonszelmann): move back to rustc_attr
 #[derive(Diagnostic)]
 #[diag(passes_rustc_const_stable_indirect_pairing)]