Skip to content

Commit 47e3bc8

Browse files
committed
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.
1 parent 55d4364 commit 47e3bc8

File tree

21 files changed

+741
-6
lines changed

21 files changed

+741
-6
lines changed

compiler/rustc_codegen_ssa/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomo
172172
codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize`
173173
.note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
174174
175+
codegen_ssa_invalid_sanitize = invalid argument for `sanitize`
176+
.note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
177+
175178
codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed
176179
177180
codegen_ssa_ld64_unimplemented_modifier = `as-needed` modifier not implemented yet for ld64

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashMap;
99
use rustc_hir::def::DefKind;
1010
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
1111
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
12-
use rustc_hir::{self as hir, HirId, LangItem, lang_items};
12+
use rustc_hir::{self as hir, Attribute, HirId, LangItem, lang_items};
1313
use rustc_middle::middle::codegen_fn_attrs::{
1414
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
1515
};
@@ -86,6 +86,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
8686
let mut inline_span = None;
8787
let mut link_ordinal_span = None;
8888
let mut no_sanitize_span = None;
89+
let mut sanitize_span = None;
8990
let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
9091
let mut no_mangle_span = None;
9192

@@ -353,6 +354,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
353354
}
354355
}
355356
}
357+
sym::sanitize => sanitize_span = Some(attr.span()),
356358
sym::instruction_set => {
357359
codegen_fn_attrs.instruction_set =
358360
attr.meta_item_list().and_then(|l| match &l[..] {
@@ -447,6 +449,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
447449
}
448450
}
449451

452+
codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did);
450453
mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
451454

452455
codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
@@ -580,6 +583,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
580583
lint.span_note(inline_span, "inlining requested here");
581584
})
582585
}
586+
if !codegen_fn_attrs.no_sanitize.is_empty()
587+
&& codegen_fn_attrs.inline.always()
588+
&& let (Some(sanitize_span), Some(inline_span)) = (sanitize_span, inline_span)
589+
{
590+
let hir_id = tcx.local_def_id_to_hir_id(did);
591+
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| {
592+
lint.primary_message("setting `sanitize` off will have no effect after inlining");
593+
lint.span_note(inline_span, "inlining requested here");
594+
})
595+
}
583596

584597
// Weak lang items have the same semantics as "std internal" symbols in the
585598
// sense that they're preserved through all our LTO passes and only
@@ -660,6 +673,84 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
660673
codegen_fn_attrs
661674
}
662675

676+
/// For an attr that has the `sanitize` attribute, read the list of
677+
/// disabled sanitizers.
678+
fn parse_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> SanitizerSet {
679+
let mut result = SanitizerSet::empty();
680+
if let Some(list) = attr.meta_item_list() {
681+
for item in list.iter() {
682+
let MetaItemInner::MetaItem(set) = item else {
683+
tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() });
684+
break;
685+
};
686+
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
687+
match segments.as_slice() {
688+
[sym::address] if set.value_str() == Some(sym::off) => {
689+
result |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
690+
}
691+
[sym::address] if set.value_str() == Some(sym::on) => {
692+
result &= !SanitizerSet::ADDRESS;
693+
result &= !SanitizerSet::KERNELADDRESS;
694+
}
695+
[sym::cfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::CFI,
696+
[sym::cfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::CFI,
697+
[sym::kcfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::KCFI,
698+
[sym::kcfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::KCFI,
699+
[sym::memory] if set.value_str() == Some(sym::off) => {
700+
result |= SanitizerSet::MEMORY
701+
}
702+
[sym::memory] if set.value_str() == Some(sym::on) => {
703+
result &= !SanitizerSet::MEMORY
704+
}
705+
[sym::memtag] if set.value_str() == Some(sym::off) => {
706+
result |= SanitizerSet::MEMTAG
707+
}
708+
[sym::memtag] if set.value_str() == Some(sym::on) => {
709+
result &= !SanitizerSet::MEMTAG
710+
}
711+
[sym::shadow_call_stack] if set.value_str() == Some(sym::off) => {
712+
result |= SanitizerSet::SHADOWCALLSTACK
713+
}
714+
[sym::shadow_call_stack] if set.value_str() == Some(sym::on) => {
715+
result &= !SanitizerSet::SHADOWCALLSTACK
716+
}
717+
[sym::thread] if set.value_str() == Some(sym::off) => {
718+
result |= SanitizerSet::THREAD
719+
}
720+
[sym::thread] if set.value_str() == Some(sym::on) => {
721+
result &= !SanitizerSet::THREAD
722+
}
723+
[sym::hwaddress] if set.value_str() == Some(sym::off) => {
724+
result |= SanitizerSet::HWADDRESS
725+
}
726+
[sym::hwaddress] if set.value_str() == Some(sym::on) => {
727+
result &= !SanitizerSet::HWADDRESS
728+
}
729+
_ => {
730+
tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() });
731+
}
732+
}
733+
}
734+
}
735+
result
736+
}
737+
738+
fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet {
739+
// Check for a sanitize annotation directly on this def.
740+
if let Some(attr) = tcx.get_attr(did, sym::sanitize) {
741+
return parse_sanitize_attr(tcx, attr);
742+
}
743+
744+
// Otherwise backtrack.
745+
match tcx.opt_local_parent(did) {
746+
// Check the parent (recursively).
747+
Some(parent) => tcx.disabled_sanitizers_for(parent),
748+
// We reached the crate root without seeing an attribute, so
749+
// there is no sanitizers to exclude.
750+
None => SanitizerSet::empty(),
751+
}
752+
}
753+
663754
/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
664755
/// combinations are allowed.
665756
pub fn check_tied_features(
@@ -900,5 +991,10 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
900991
}
901992

902993
pub(crate) fn provide(providers: &mut Providers) {
903-
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
994+
*providers = Providers {
995+
codegen_fn_attrs,
996+
should_inherit_track_caller,
997+
disabled_sanitizers_for,
998+
..*providers
999+
};
9041000
}

compiler/rustc_codegen_ssa/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,14 @@ pub(crate) struct InvalidNoSanitize {
11441144
pub span: Span,
11451145
}
11461146

1147+
#[derive(Diagnostic)]
1148+
#[diag(codegen_ssa_invalid_sanitize)]
1149+
#[note]
1150+
pub(crate) struct InvalidSanitize {
1151+
#[primary_span]
1152+
pub span: Span,
1153+
}
1154+
11471155
#[derive(Diagnostic)]
11481156
#[diag(codegen_ssa_invalid_link_ordinal_nargs)]
11491157
#[note]

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
526526
template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
527527
EncodeCrossCrate::No, experimental!(no_sanitize)
528528
),
529+
gated!(
530+
sanitize, Normal, template!(List: r#"address = "on|off", cfi = "on|off""#), ErrorPreceding,
531+
EncodeCrossCrate::No, sanitize, experimental!(sanitize),
532+
),
529533
gated!(
530534
coverage, Normal, template!(OneOf: &[sym::off, sym::on]),
531535
ErrorPreceding, EncodeCrossCrate::No,

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,8 @@ declare_features! (
627627
(unstable, return_type_notation, "1.70.0", Some(109417)),
628628
/// Allows `extern "rust-cold"`.
629629
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
630+
/// Allows the use of the `sanitize` attribute.
631+
(unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)),
630632
/// Allows the use of SIMD types in functions declared in `extern` blocks.
631633
(unstable, simd_ffi, "1.0.0", Some(27731)),
632634
/// Allows specialization of implementations (RFC 1210).

compiler/rustc_middle/src/query/erase.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ trivial! {
342342
rustc_span::Symbol,
343343
rustc_span::Ident,
344344
rustc_target::spec::PanicStrategy,
345+
rustc_target::spec::SanitizerSet,
345346
rustc_type_ir::Variance,
346347
u32,
347348
usize,

compiler/rustc_middle/src/query/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ use rustc_session::lint::LintExpectationId;
9999
use rustc_span::def_id::LOCAL_CRATE;
100100
use rustc_span::source_map::Spanned;
101101
use rustc_span::{DUMMY_SP, Span, Symbol};
102-
use rustc_target::spec::PanicStrategy;
102+
use rustc_target::spec::{PanicStrategy, SanitizerSet};
103103
use {rustc_abi as abi, rustc_ast as ast, rustc_attr_data_structures as attr, rustc_hir as hir};
104104

105105
use crate::infer::canonical::{self, Canonical};
@@ -2653,6 +2653,16 @@ rustc_queries! {
26532653
desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) }
26542654
separate_provide_extern
26552655
}
2656+
2657+
/// Checks for the nearest `#[sanitize(xyz = "off")]` or
2658+
/// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the
2659+
/// crate root.
2660+
///
2661+
/// Returns the set of sanitizers that is explicitly disabled for this def.
2662+
query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet {
2663+
desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) }
2664+
feedable
2665+
}
26562666
}
26572667

26582668
rustc_with_all_queries! { define_callbacks! }

compiler/rustc_passes/messages.ftl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,12 @@ passes_rustc_std_internal_symbol =
654654
attribute should be applied to functions or statics
655655
.label = not a function or static
656656
657+
passes_sanitize_attribute_not_allowed =
658+
sanitize attribute not allowed here
659+
.not_fn_impl_mod = not a function, impl block, or module
660+
.no_body = function has no body
661+
.help = sanitize attribute can be applied to a function (with body), impl block, or module
662+
657663
passes_should_be_applied_to_fn =
658664
attribute should be applied to a function definition
659665
.label = {$on_crate ->

compiler/rustc_passes/src/check_attr.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
164164
[sym::no_sanitize, ..] => {
165165
self.check_no_sanitize(attr, span, target)
166166
}
167+
[sym::sanitize, ..] => {
168+
self.check_sanitize(attr, span, target)
169+
}
167170
[sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item),
168171
[sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
169172
[sym::target_feature, ..] => {
@@ -570,6 +573,46 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
570573
}
571574
}
572575

576+
/// Checks that the `#[sanitize(..)]` attribute is applied to a
577+
/// function/closure/method, or to an impl block or module.
578+
fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) {
579+
let mut not_fn_impl_mod = None;
580+
let mut no_body = None;
581+
582+
if let Some(list) = attr.meta_item_list() {
583+
for item in list.iter() {
584+
let MetaItemInner::MetaItem(set) = item else {
585+
return;
586+
};
587+
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
588+
match target {
589+
Target::Fn
590+
| Target::Closure
591+
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
592+
| Target::Impl
593+
| Target::Mod => return,
594+
Target::Static if matches!(segments.as_slice(), [sym::address]) => return,
595+
596+
// These are "functions", but they aren't allowed because they don't
597+
// have a body, so the usual explanation would be confusing.
598+
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
599+
no_body = Some(target_span);
600+
}
601+
602+
_ => {
603+
not_fn_impl_mod = Some(target_span);
604+
}
605+
}
606+
}
607+
self.dcx().emit_err(errors::SanitizeAttributeNotAllowed {
608+
attr_span: attr.span(),
609+
not_fn_impl_mod,
610+
no_body,
611+
help: (),
612+
});
613+
}
614+
}
615+
573616
fn check_generic_attr(
574617
&self,
575618
hir_id: HirId,

compiler/rustc_passes/src/errors.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,6 +1754,23 @@ pub(crate) struct NoSanitize<'a> {
17541754
pub attr_str: &'a str,
17551755
}
17561756

1757+
/// "sanitize attribute not allowed here"
1758+
#[derive(Diagnostic)]
1759+
#[diag(passes_sanitize_attribute_not_allowed)]
1760+
pub(crate) struct SanitizeAttributeNotAllowed {
1761+
#[primary_span]
1762+
pub attr_span: Span,
1763+
/// "not a function, impl block, or module"
1764+
#[label(passes_not_fn_impl_mod)]
1765+
pub not_fn_impl_mod: Option<Span>,
1766+
/// "function has no body"
1767+
#[label(passes_no_body)]
1768+
pub no_body: Option<Span>,
1769+
/// "sanitize attribute can be applied to a function (with body), impl block, or module"
1770+
#[help]
1771+
pub help: (),
1772+
}
1773+
17571774
// FIXME(jdonszelmann): move back to rustc_attr
17581775
#[derive(Diagnostic)]
17591776
#[diag(passes_rustc_const_stable_indirect_pairing)]

0 commit comments

Comments
 (0)