From ab9c644aa71a246f54d200f291933ef1e311e674 Mon Sep 17 00:00:00 2001 From: Lucas Baumann Date: Sat, 26 Jul 2025 12:02:35 +0200 Subject: [PATCH 1/4] replace SanitizerSet in CodegenFnAttrs by new type --- compiler/rustc_codegen_llvm/src/attributes.rs | 9 +++--- compiler/rustc_codegen_llvm/src/base.rs | 8 ++--- compiler/rustc_codegen_llvm/src/builder.rs | 4 +-- .../rustc_codegen_ssa/src/codegen_attrs.rs | 30 +++++++++---------- compiler/rustc_middle/src/lib.rs | 2 ++ .../src/middle/codegen_fn_attrs.rs | 20 ++++++++++--- compiler/rustc_middle/src/query/erase.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 8 ++--- compiler/rustc_mir_transform/src/inline.rs | 2 +- tests/ui/sanitizer/inline-always-sanitize.rs | 2 +- .../sanitizer/inline-always-sanitize.stderr | 2 +- 11 files changed, 52 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 209b8efa2c3b3..fc6ad090d534e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -2,7 +2,7 @@ use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{ - CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, + CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, }; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet}; @@ -98,10 +98,11 @@ fn patchable_function_entry_attrs<'ll>( pub(crate) fn sanitize_attrs<'ll, 'tcx>( cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>, - no_sanitize: SanitizerSet, + sanitizer_fn_attr: SanitizerFnAttrs, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - let enabled = tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; + // TODO: add realtim sanitizer + let enabled = tcx.sess.opts.unstable_opts.sanitizer - sanitizer_fn_attr.disabled; if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); } @@ -417,7 +418,7 @@ 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.no_sanitize)); + to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers)); // For non-naked functions, set branch protection attributes on aarch64. if let Some(BranchProtection { bti, pac_ret, gcs }) = diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 4523d629b1ef6..6cbddfec46318 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -20,7 +20,7 @@ use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::attrs::Linkage; use rustc_middle::dep_graph; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::DebugInfo; @@ -105,7 +105,7 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerSet::empty()); + let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } @@ -191,10 +191,10 @@ pub(crate) fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility { } pub(crate) fn set_variable_sanitizer_attrs(llval: &Value, attrs: &CodegenFnAttrs) { - if attrs.no_sanitize.contains(SanitizerSet::ADDRESS) { + if attrs.sanitizers.disabled.contains(SanitizerSet::ADDRESS) { unsafe { llvm::LLVMRustSetNoSanitizeAddress(llval) }; } - if attrs.no_sanitize.contains(SanitizerSet::HWADDRESS) { + if attrs.sanitizers.disabled.contains(SanitizerSet::HWADDRESS) { unsafe { llvm::LLVMRustSetNoSanitizeHWAddress(llval) }; } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index c082a82306848..bcca90046a4ce 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1803,7 +1803,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { && is_indirect_call { if let Some(fn_attrs) = fn_attrs - && fn_attrs.no_sanitize.contains(SanitizerSet::CFI) + && fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI) { return; } @@ -1861,7 +1861,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { && is_indirect_call { if let Some(fn_attrs) = fn_attrs - && fn_attrs.no_sanitize.contains(SanitizerSet::KCFI) + && fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI) { return None; } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 44de48a3ada51..5dd1365f26458 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -8,7 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items}; use rustc_middle::middle::codegen_fn_attrs::{ - CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, + CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, }; use rustc_middle::query::Providers; use rustc_middle::span_bug; @@ -16,7 +16,6 @@ use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::{Ident, Span, sym}; -use rustc_target::spec::SanitizerSet; use crate::errors; use crate::target_features::{ @@ -351,7 +350,8 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment); // Compute the disabled sanitizers. - codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did); + codegen_fn_attrs.sanitizers.disabled |= + tcx.sanitizer_settings_for(did).disabled; // On trait methods, inherit the `#[align]` of the trait's method prototype. codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); @@ -455,14 +455,14 @@ fn check_result( } // warn that inline has no effect when no_sanitize is present - if !codegen_fn_attrs.no_sanitize.is_empty() + if codegen_fn_attrs.sanitizers != SanitizerFnAttrs::default() && codegen_fn_attrs.inline.always() - && let (Some(no_sanitize_span), Some(inline_span)) = + && let (Some(sanitize_span), Some(inline_span)) = (interesting_spans.sanitize, interesting_spans.inline) { let hir_id = tcx.local_def_id_to_hir_id(did); - tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| { - lint.primary_message("setting `sanitize` off will have no effect after inlining"); + tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| { + lint.primary_message("non-default `sanitize` will have no effect after inlining"); lint.span_note(inline_span, "inlining requested here"); }) } @@ -576,14 +576,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs } -fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { +fn sanitizer_settings_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerFnAttrs { // Backtrack to the crate root. - let mut disabled = match tcx.opt_local_parent(did) { + let mut settings = match tcx.opt_local_parent(did) { // Check the parent (recursively). - Some(parent) => tcx.disabled_sanitizers_for(parent), + Some(parent) => tcx.sanitizer_settings_for(parent), // We reached the crate root without seeing an attribute, so // there is no sanitizers to exclude. - None => SanitizerSet::empty(), + None => SanitizerFnAttrs::default(), }; // Check for a sanitize annotation directly on this def. @@ -591,15 +591,15 @@ fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { { // the on set is the set of sanitizers explicitly enabled. // we mask those out since we want the set of disabled sanitizers here - disabled &= !*on_set; + settings.disabled &= !*on_set; // the off set is the set of sanitizers explicitly disabled. // we or those in here. - disabled |= *off_set; + settings.disabled |= *off_set; // the on set and off set are distjoint since there's a third option: unset. // a node may not set the sanitizer setting in which case it inherits from parents. // the code above in this function does this backtracking } - disabled + settings } /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller @@ -731,7 +731,7 @@ pub(crate) fn provide(providers: &mut Providers) { codegen_fn_attrs, should_inherit_track_caller, inherited_align, - disabled_sanitizers_for, + sanitizer_settings_for, ..*providers }; } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 754a258eef93d..708566130d76d 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -38,6 +38,8 @@ #![feature(box_as_ptr)] #![feature(box_patterns)] #![feature(closure_track_caller)] +#![feature(const_default)] +#![feature(const_trait_impl)] #![feature(core_intrinsics)] #![feature(debug_closure_helpers)] #![feature(decl_macro)] diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index d47d811610a74..fd1f5791de4d0 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -80,9 +80,9 @@ pub struct CodegenFnAttrs { /// The `#[link_section = "..."]` attribute, or what executable section this /// should be placed in. pub link_section: Option, - /// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which - /// instrumentation should be disabled inside the function. - pub no_sanitize: SanitizerSet, + /// The `#[sanitize(xyz = "off")]` attribute. Indicates the settings for each + /// sanitizer for this function. + pub sanitizers: SanitizerFnAttrs, /// The `#[instruction_set(set)]` attribute. Indicates if the generated code should /// be generated against a specific instruction set. Only usable on architectures which allow /// switching between multiple instruction sets. @@ -209,7 +209,7 @@ impl CodegenFnAttrs { linkage: None, import_linkage: None, link_section: None, - no_sanitize: SanitizerSet::empty(), + sanitizers: SanitizerFnAttrs::default(), instruction_set: None, alignment: None, patchable_function_entry: None, @@ -241,3 +241,15 @@ impl CodegenFnAttrs { } } } + +#[derive(Clone, Copy, Debug, HashStable, TyEncodable, TyDecodable, Eq, PartialEq)] +pub struct SanitizerFnAttrs { + pub disabled: SanitizerSet, +} + +impl const Default for SanitizerFnAttrs { + fn default() -> Self { + Self { disabled: SanitizerSet::empty() } + } + +} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index ad82e83ee40b2..ffde4603954dd 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -344,6 +344,7 @@ trivial! { rustc_middle::ty::UnusedGenericParams, rustc_middle::ty::util::AlwaysRequiresDrop, rustc_middle::ty::Visibility, + rustc_middle::middle::codegen_fn_attrs::SanitizerFnAttrs, rustc_session::config::CrateType, rustc_session::config::EntryFnType, rustc_session::config::OptLevel, @@ -361,7 +362,6 @@ trivial! { rustc_span::Symbol, rustc_span::Ident, rustc_target::spec::PanicStrategy, - rustc_target::spec::SanitizerSet, rustc_type_ir::Variance, u32, usize, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 588ff68ba572d..ce4fa6add5c1e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -97,7 +97,7 @@ use rustc_session::lint::LintExpectationId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_target::spec::{PanicStrategy, SanitizerSet}; +use rustc_target::spec::PanicStrategy; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; pub use self::keys::{AsLocalKey, Key, LocalCrate}; @@ -105,7 +105,7 @@ pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsur use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; -use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::middle::deduced_param_attrs::DeducedParamAttrs; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; @@ -2723,8 +2723,8 @@ rustc_queries! { /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the /// crate root. /// - /// Returns the set of sanitizers that is explicitly disabled for this def. - query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet { + /// Returns the sanitizer settings for this def. + query sanitizer_settings_for(key: LocalDefId) -> SanitizerFnAttrs { desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } feedable } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index dc6088849bf5e..793060f626e41 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -818,7 +818,7 @@ fn check_codegen_attributes<'tcx, I: Inliner<'tcx>>( } let codegen_fn_attrs = tcx.codegen_fn_attrs(inliner.caller_def_id()); - if callee_attrs.no_sanitize != codegen_fn_attrs.no_sanitize { + if callee_attrs.sanitizers != codegen_fn_attrs.sanitizers { return Err("incompatible sanitizer set"); } diff --git a/tests/ui/sanitizer/inline-always-sanitize.rs b/tests/ui/sanitizer/inline-always-sanitize.rs index d6ee214e9b374..97bb60df72a6d 100644 --- a/tests/ui/sanitizer/inline-always-sanitize.rs +++ b/tests/ui/sanitizer/inline-always-sanitize.rs @@ -5,7 +5,7 @@ #[inline(always)] //~^ NOTE inlining requested here #[sanitize(address = "off")] -//~^ WARN setting `sanitize` off will have no effect after inlining +//~^ WARN non-default `sanitize` will have no effect after inlining //~| NOTE on by default fn x() { } diff --git a/tests/ui/sanitizer/inline-always-sanitize.stderr b/tests/ui/sanitizer/inline-always-sanitize.stderr index ed47947216950..fbfe5378c0922 100644 --- a/tests/ui/sanitizer/inline-always-sanitize.stderr +++ b/tests/ui/sanitizer/inline-always-sanitize.stderr @@ -1,4 +1,4 @@ -warning: setting `sanitize` off will have no effect after inlining +warning: non-default `sanitize` will have no effect after inlining --> $DIR/inline-always-sanitize.rs:7:1 | LL | #[sanitize(address = "off")] From bc2ca26a4d14ab6492c36baabd316d8b37d3042d Mon Sep 17 00:00:00 2001 From: Lucas Baumann Date: Fri, 23 May 2025 14:35:28 +0200 Subject: [PATCH 2/4] add realtime sanitizer --- .../src/attributes/codegen_attrs.rs | 20 +++++++++-- compiler/rustc_codegen_llvm/src/attributes.rs | 15 ++++++-- compiler/rustc_codegen_llvm/src/back/write.rs | 1 + compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 ++ compiler/rustc_codegen_ssa/src/back/link.rs | 3 ++ .../rustc_codegen_ssa/src/codegen_attrs.rs | 18 +++++++--- .../rustc_hir/src/attrs/data_structures.rs | 18 +++++++++- compiler/rustc_hir/src/lib.rs | 3 ++ .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 9 +++++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ++++ .../src/middle/codegen_fn_attrs.rs | 6 ++-- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_session/src/options.rs | 3 +- compiler/rustc_span/src/symbol.rs | 4 +++ compiler/rustc_target/src/spec/mod.rs | 3 ++ .../src/spec/targets/aarch64_apple_darwin.rs | 5 ++- .../src/spec/targets/aarch64_apple_ios.rs | 4 ++- .../src/spec/targets/aarch64_apple_ios_sim.rs | 4 ++- .../spec/targets/aarch64_unknown_linux_gnu.rs | 3 +- .../src/spec/targets/x86_64_apple_darwin.rs | 3 +- .../spec/targets/x86_64_unknown_linux_gnu.rs | 3 +- src/bootstrap/download-ci-llvm-stamp | 2 +- src/bootstrap/src/core/build_steps/llvm.rs | 18 +++++----- src/doc/rustc-dev-guide/src/sanitizers.md | 2 +- src/tools/compiletest/src/common.rs | 1 + .../src/directives/directive_names.rs | 1 + src/tools/compiletest/src/directives/needs.rs | 7 ++++ tests/ui/attributes/malformed-attrs.stderr | 2 +- tests/ui/check-cfg/well-known-values.stderr | 2 +- tests/ui/sanitize-attr/invalid-sanitize.rs | 3 ++ .../ui/sanitize-attr/invalid-sanitize.stderr | 36 +++++++++++++++---- tests/ui/sanitizer/realtime-alloc.rs | 19 ++++++++++ tests/ui/sanitizer/realtime-blocking.rs | 24 +++++++++++++ tests/ui/sanitizer/realtime-caller.rs | 27 ++++++++++++++ 34 files changed, 240 insertions(+), 40 deletions(-) create mode 100644 tests/ui/sanitizer/realtime-alloc.rs create mode 100644 tests/ui/sanitizer/realtime-blocking.rs create mode 100644 tests/ui/sanitizer/realtime-caller.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index f09b02251e4d0..3c6db4b1e46f6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy}; +use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy}; use rustc_session::parse::feature_err; use super::prelude::*; @@ -592,7 +592,8 @@ impl SingleAttributeParser for SanitizeParser { r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, - r#"thread = "on|off""# + r#"thread = "on|off""#, + r#"realtime = "nonblocking|blocking|caller""#, ]); const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; @@ -606,6 +607,7 @@ impl SingleAttributeParser for SanitizeParser { let mut on_set = SanitizerSet::empty(); let mut off_set = SanitizerSet::empty(); + let mut rtsan = None; for item in list.mixed() { let Some(item) = item.meta_item() else { @@ -654,6 +656,17 @@ impl SingleAttributeParser for SanitizeParser { Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK), Some(sym::thread) => apply(SanitizerSet::THREAD), Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS), + Some(sym::realtime) => match value.value_as_str() { + Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking), + Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking), + Some(sym::caller) => rtsan = Some(RtsanSetting::Caller), + _ => { + cx.expected_specific_argument_strings( + value.value_span, + &[sym::nonblocking, sym::blocking, sym::caller], + ); + } + }, _ => { cx.expected_specific_argument_strings( item.path().span(), @@ -666,6 +679,7 @@ impl SingleAttributeParser for SanitizeParser { sym::shadow_call_stack, sym::thread, sym::hwaddress, + sym::realtime, ], ); continue; @@ -673,6 +687,6 @@ impl SingleAttributeParser for SanitizeParser { } } - Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span }) + Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span }) } } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index fc6ad090d534e..7f53bdfee237d 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -1,5 +1,5 @@ //! Set and unset common attributes on LLVM values. -use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr, RtsanSetting}; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, @@ -101,7 +101,6 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>( sanitizer_fn_attr: SanitizerFnAttrs, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - // TODO: add realtim sanitizer let enabled = tcx.sess.opts.unstable_opts.sanitizer - sanitizer_fn_attr.disabled; if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); @@ -132,6 +131,18 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>( if enabled.contains(SanitizerSet::SAFESTACK) { attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx)); } + if tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::REALTIME) { + match sanitizer_fn_attr.rtsan_setting { + RtsanSetting::Nonblocking => { + attrs.push(llvm::AttributeKind::SanitizeRealtimeNonblocking.create_attr(cx.llcx)) + } + RtsanSetting::Blocking => { + attrs.push(llvm::AttributeKind::SanitizeRealtimeBlocking.create_attr(cx.llcx)) + } + // caller is the default, so no llvm attribute + RtsanSetting::Caller => (), + } + } attrs } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 6ce1cbe072c06..11a61e1cef56b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -646,6 +646,7 @@ pub(crate) unsafe fn llvm_optimize( sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY), sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY), sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int, + sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME), sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS), sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 284e2ec50f5ee..b60a053d71e6a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -289,6 +289,8 @@ pub(crate) enum AttributeKind { DeadOnUnwind = 43, DeadOnReturn = 44, CapturesReadOnly = 45, + SanitizeRealtimeNonblocking = 46, + SanitizeRealtimeBlocking = 47, } /// LLVMIntPredicate @@ -481,6 +483,7 @@ pub(crate) struct SanitizerOptions { pub sanitize_memory: bool, pub sanitize_memory_recover: bool, pub sanitize_memory_track_origins: c_int, + pub sanitize_realtime: bool, pub sanitize_thread: bool, pub sanitize_hwaddress: bool, pub sanitize_hwaddress_recover: bool, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ea538d3d46981..4f8b5afb19cf3 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1251,6 +1251,9 @@ fn add_sanitizer_libraries( if sanitizer.contains(SanitizerSet::SAFESTACK) { link_sanitizer_runtime(sess, flavor, linker, "safestack"); } + if sanitizer.contains(SanitizerSet::REALTIME) { + link_sanitizer_runtime(sess, flavor, linker, "rtsan"); + } } fn link_sanitizer_runtime( diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 5dd1365f26458..c80b1b2797e73 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; -use rustc_hir::attrs::{AttributeKind, InlineAttr, InstructionSetAttr, UsedBy}; +use rustc_hir::attrs::{AttributeKind, InlineAttr, InstructionSetAttr, RtsanSetting, UsedBy}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items}; @@ -349,9 +349,12 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment); - // Compute the disabled sanitizers. - codegen_fn_attrs.sanitizers.disabled |= - tcx.sanitizer_settings_for(did).disabled; + // Per sanitizer override if it's in the default state + let sanitizers = tcx.sanitizer_settings_for(did); + codegen_fn_attrs.sanitizers.disabled |= sanitizers.disabled; + if codegen_fn_attrs.sanitizers.rtsan_setting == RtsanSetting::default() { + codegen_fn_attrs.sanitizers.rtsan_setting = sanitizers.rtsan_setting; + } // On trait methods, inherit the `#[align]` of the trait's method prototype. codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); @@ -587,7 +590,7 @@ fn sanitizer_settings_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerFnAttrs }; // Check for a sanitize annotation directly on this def. - if let Some((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did), AttributeKind::Sanitize {on_set, off_set, ..} => (on_set, off_set)) + if let Some((on_set, off_set, rtsan)) = find_attr!(tcx.get_all_attrs(did), AttributeKind::Sanitize {on_set, off_set, rtsan, ..} => (on_set, off_set, rtsan)) { // the on set is the set of sanitizers explicitly enabled. // we mask those out since we want the set of disabled sanitizers here @@ -598,6 +601,11 @@ fn sanitizer_settings_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerFnAttrs // the on set and off set are distjoint since there's a third option: unset. // a node may not set the sanitizer setting in which case it inherits from parents. // the code above in this function does this backtracking + + // if rtsan was specified here override the parent + if let Some(rtsan) = rtsan { + settings.rtsan_setting = *rtsan; + } } settings } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 4b7894f88e3b7..e8d336c299368 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -382,6 +382,16 @@ pub struct DebugVisualizer { pub path: Symbol, } +#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)] +#[derive(HashStable_Generic, PrintAttribute)] +#[derive_const(Default)] +pub enum RtsanSetting { + Nonblocking, + Blocking, + #[default] + Caller, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -683,7 +693,13 @@ pub enum AttributeKind { /// /// the on set and off set are distjoint since there's a third option: unset. /// a node may not set the sanitizer setting in which case it inherits from parents. - Sanitize { on_set: SanitizerSet, off_set: SanitizerSet, span: Span }, + /// rtsan is unset if None + Sanitize { + on_set: SanitizerSet, + off_set: SanitizerSet, + rtsan: Option, + span: Span, + }, /// Represents `#[should_panic]` ShouldPanic { reason: Option, span: Span }, diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index eb630fc801863..2fc2572c80924 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -5,7 +5,10 @@ // tidy-alphabetical-start #![feature(associated_type_defaults)] #![feature(closure_track_caller)] +#![feature(const_default)] +#![feature(const_trait_impl)] #![feature(debug_closure_helpers)] +#![feature(derive_const)] #![feature(exhaustive_patterns)] #![feature(never_type)] #![feature(variant_count)] diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 8d9c4b48b5c4a..e1952190b2fe1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -38,6 +38,7 @@ #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" +#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Scalar/AnnotationRemarks.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h" @@ -518,6 +519,7 @@ struct LLVMRustSanitizerOptions { bool SanitizeMemory; bool SanitizeMemoryRecover; int SanitizeMemoryTrackOrigins; + bool SanitizerRealtime; bool SanitizeThread; bool SanitizeHWAddress; bool SanitizeHWAddressRecover; @@ -773,6 +775,13 @@ extern "C" LLVMRustResult LLVMRustOptimize( MPM.addPass(HWAddressSanitizerPass(opts)); }); } + if (SanitizerOptions->SanitizerRealtime) { + OptimizerLastEPCallbacks.push_back([](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + MPM.addPass(RealtimeSanitizerPass()); + }); + } } ModulePassManager MPM; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6b4f8a6dba79f..e99e74c70854e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -245,6 +245,8 @@ enum class LLVMRustAttributeKind { DeadOnUnwind = 43, DeadOnReturn = 44, CapturesReadOnly = 45, + SanitizeRealtimeNonblocking = 46, + SanitizeRealtimeBlocking = 47, }; static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { @@ -340,6 +342,10 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { case LLVMRustAttributeKind::CapturesAddress: case LLVMRustAttributeKind::CapturesReadOnly: report_fatal_error("Should be handled separately"); + case LLVMRustAttributeKind::SanitizeRealtimeNonblocking: + return Attribute::SanitizeRealtime; + case LLVMRustAttributeKind::SanitizeRealtimeBlocking: + return Attribute::SanitizeRealtimeBlocking; } report_fatal_error("bad LLVMRustAttributeKind"); } diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index fd1f5791de4d0..5a28d56d4e549 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use rustc_abi::Align; -use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, Linkage, OptimizeAttr}; +use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, Linkage, OptimizeAttr, RtsanSetting}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; @@ -245,11 +245,11 @@ impl CodegenFnAttrs { #[derive(Clone, Copy, Debug, HashStable, TyEncodable, TyDecodable, Eq, PartialEq)] pub struct SanitizerFnAttrs { pub disabled: SanitizerSet, + pub rtsan_setting: RtsanSetting, } impl const Default for SanitizerFnAttrs { fn default() -> Self { - Self { disabled: SanitizerSet::empty() } + Self { disabled: SanitizerSet::empty(), rtsan_setting: RtsanSetting::default() } } - } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ef42c42f68b37..856d587d452fd 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -212,7 +212,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => { self.check_custom_mir(dialect, phase, attr_span) } - &Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, span: attr_span}) => { + &Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, rtsan: _, span: attr_span}) => { self.check_sanitize(attr_span, on_set | off_set, span, target); }, Attribute::Parsed(AttributeKind::Link(_, attr_span)) => { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6dd90546de1b0..c76cec00f2528 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -808,7 +808,7 @@ mod desc { pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; pub(crate) const parse_oom_strategy: &str = "either `panic` or `abort`"; pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, or `thread`"; + pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'"; pub(crate) const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub(crate) const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -1254,6 +1254,7 @@ pub mod parse { "thread" => SanitizerSet::THREAD, "hwaddress" => SanitizerSet::HWADDRESS, "safestack" => SanitizerSet::SAFESTACK, + "realtime" => SanitizerSet::REALTIME, _ => return false, } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ef72c478951b8..068ce95c7cdc0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -578,6 +578,7 @@ symbols! { bitxor_assign, black_box, block, + blocking, bool, bool_then, borrowck_graphviz_format, @@ -610,6 +611,7 @@ symbols! { call_once, call_once_future, call_ref_future, + caller, caller_location, capture_disjoint_fields, carrying_mul_add, @@ -1543,6 +1545,7 @@ symbols! { non_exhaustive_omitted_patterns_lint, non_lifetime_binders, non_modrs_mods, + nonblocking, none, nontemporal_store, noop_method_borrow, @@ -1777,6 +1780,7 @@ symbols! { read_via_copy, readonly, realloc, + realtime, reason, reborrow, receiver, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1638c87c9ca9a..45df17549c269 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1177,6 +1177,7 @@ bitflags::bitflags! { const KERNELADDRESS = 1 << 9; const SAFESTACK = 1 << 10; const DATAFLOW = 1 << 11; + const REALTIME = 1 << 12; } } rustc_data_structures::external_bitflags_debug! { SanitizerSet } @@ -1227,6 +1228,7 @@ impl SanitizerSet { SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack", SanitizerSet::THREAD => "thread", SanitizerSet::HWADDRESS => "hwaddress", + SanitizerSet::REALTIME => "realtime", _ => return None, }) } @@ -1271,6 +1273,7 @@ impl FromStr for SanitizerSet { "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK, "thread" => SanitizerSet::THREAD, "hwaddress" => SanitizerSet::HWADDRESS, + "realtime" => SanitizerSet::REALTIME, s => return Err(format!("unknown sanitizer {s}")), }) } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs index e19604725559a..ecfd6ebba8638 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs @@ -20,7 +20,10 @@ pub(crate) fn target() -> Target { cpu: "apple-m1".into(), max_atomic_width: Some(128), // FIXME: The leak sanitizer currently fails the tests, see #88132. - supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD, + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, supports_xray: true, ..opts }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs index 3b522c34522b0..85b4a0614a85c 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs @@ -18,7 +18,9 @@ pub(crate) fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::THREAD, + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, ..opts }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs index d366ed264820b..2924ba5ebb105 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs @@ -18,7 +18,9 @@ pub(crate) fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::THREAD, + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, ..opts }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs index 4220d74dfc890..5f123ed9e37fc 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs @@ -30,7 +30,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::MEMORY | SanitizerSet::MEMTAG | SanitizerSet::THREAD - | SanitizerSet::HWADDRESS, + | SanitizerSet::HWADDRESS + | SanitizerSet::REALTIME, supports_xray: true, ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs index 8892c50d84478..cb517b2a357a5 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs @@ -21,7 +21,8 @@ pub(crate) fn target() -> Target { supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK - | SanitizerSet::THREAD, + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, supports_xray: true, ..opts }, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index 59bb7f5962101..39c30d66ab353 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -17,7 +17,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::SAFESTACK - | SanitizerSet::THREAD; + | SanitizerSet::THREAD + | SanitizerSet::REALTIME; base.supports_xray = true; Target { diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index b70d452b427c8..e79391a107aac 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/139931 +Last change is for: https://github.com/rust-lang/rust/pull/147935 diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index d43d261ad6c3f..7e1b59351e042 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1262,13 +1262,13 @@ fn supported_sanitizers( }; match &*target.triple { - "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), - "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan"]), - "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan"]), + "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]), + "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan", "rtsan"]), + "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan", "rtsan"]), "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { - common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) + common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan"]) } "aarch64-unknown-linux-ohos" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) @@ -1276,7 +1276,7 @@ fn supported_sanitizers( "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => { common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"]) } - "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), + "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]), "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]), "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]), @@ -1286,9 +1286,11 @@ fn supported_sanitizers( } "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]), "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]), - "x86_64-unknown-linux-gnu" => { - common_libs("linux", "x86_64", &["asan", "dfsan", "lsan", "msan", "safestack", "tsan"]) - } + "x86_64-unknown-linux-gnu" => common_libs( + "linux", + "x86_64", + &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan"], + ), "x86_64-unknown-linux-musl" => { common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) } diff --git a/src/doc/rustc-dev-guide/src/sanitizers.md b/src/doc/rustc-dev-guide/src/sanitizers.md index 34c78d4d952d5..3c6bc702557b6 100644 --- a/src/doc/rustc-dev-guide/src/sanitizers.md +++ b/src/doc/rustc-dev-guide/src/sanitizers.md @@ -45,7 +45,7 @@ implementation: [marked][sanitizer-attribute] with appropriate LLVM attribute: `SanitizeAddress`, `SanitizeHWAddress`, `SanitizeMemory`, or `SanitizeThread`. By default all functions are instrumented, but this - behaviour can be changed with `#[sanitize(xyz = "on|off")]`. + behaviour can be changed with `#[sanitize(xyz = "on|off|")]`. * The decision whether to perform instrumentation or not is possible only at a function granularity. In the cases were those decision differ between diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 58330ed20dc88..6a590fc2f2541 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -174,6 +174,7 @@ pub enum Sanitizer { ShadowCallStack, Thread, Hwaddress, + Realtime, } #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 1ab73fd87e1d0..2ab1b19fbe0e9 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -164,6 +164,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-sanitizer-leak", "needs-sanitizer-memory", "needs-sanitizer-memtag", + "needs-sanitizer-realtime", "needs-sanitizer-safestack", "needs-sanitizer-shadow-call-stack", "needs-sanitizer-support", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 5e9fe59d8d1c3..713a63317b721 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -69,6 +69,11 @@ pub(super) fn handle_needs( condition: cache.sanitizer_memtag, ignore_reason: "ignored on targets without memory tagging sanitizer", }, + Need { + name: "needs-sanitizer-realtime", + condition: cache.sanitizer_realtime, + ignore_reason: "ignored on targets without realtime sanitizer", + }, Need { name: "needs-sanitizer-shadow-call-stack", condition: cache.sanitizer_shadow_call_stack, @@ -320,6 +325,7 @@ pub(super) struct CachedNeedsConditions { sanitizer_thread: bool, sanitizer_hwaddress: bool, sanitizer_memtag: bool, + sanitizer_realtime: bool, sanitizer_shadow_call_stack: bool, sanitizer_safestack: bool, xray: bool, @@ -346,6 +352,7 @@ impl CachedNeedsConditions { sanitizer_thread: sanitizers.contains(&Sanitizer::Thread), sanitizer_hwaddress: sanitizers.contains(&Sanitizer::Hwaddress), sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag), + sanitizer_realtime: sanitizers.contains(&Sanitizer::Realtime), sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack), sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack), xray: config.target_cfg().xray, diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index c29bd0245bf04..12fede82d5f71 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -513,7 +513,7 @@ LL | #[sanitize(hwaddress = "on|off")] | ++++++++++++++++++++++ LL | #[sanitize(kcfi = "on|off")] | +++++++++++++++++ - = and 5 other candidates + = and 6 other candidates error[E0565]: malformed `no_implicit_prelude` attribute input --> $DIR/malformed-attrs.rs:101:1 diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 3f14c7b08eac6..77d45b43f361b 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -120,7 +120,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | sanitize = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `sanitize` are: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, and `thread` + = note: expected values for `sanitize` are: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `realtime`, `safestack`, `shadow-call-stack`, and `thread` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/sanitize-attr/invalid-sanitize.rs b/tests/ui/sanitize-attr/invalid-sanitize.rs index 957ce780ad0fd..707e1a3be7777 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.rs +++ b/tests/ui/sanitize-attr/invalid-sanitize.rs @@ -19,3 +19,6 @@ fn name_value() {} #[sanitize] //~ ERROR malformed `sanitize` attribute input fn just_word() {} + +#[sanitize(realtime = "on")] //~ ERROR malformed `sanitize` attribute input +fn wrong_value_realtime() {} diff --git a/tests/ui/sanitize-attr/invalid-sanitize.stderr b/tests/ui/sanitize-attr/invalid-sanitize.stderr index ec0a93be1420e..08aa5257f3f09 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.stderr +++ b/tests/ui/sanitize-attr/invalid-sanitize.stderr @@ -4,7 +4,7 @@ error[E0539]: malformed `sanitize` attribute input LL | #[sanitize(brontosaurus = "off")] | ^^^^^^^^^^^------------^^^^^^^^^^ | | - | valid arguments are "address", "cfi", "kcfi", "memory", "memtag", "shadow_call_stack", "thread" or "hwaddress" + | valid arguments are "address", "cfi", "kcfi", "memory", "memtag", "shadow_call_stack", "thread", "hwaddress" or "realtime" | help: try changing it to one of the following valid forms of the attribute | @@ -20,7 +20,7 @@ LL + #[sanitize(hwaddress = "on|off")] LL - #[sanitize(brontosaurus = "off")] LL + #[sanitize(kcfi = "on|off")] | - = and 5 other candidates + = and 6 other candidates error: multiple `sanitize` attributes --> $DIR/invalid-sanitize.rs:6:1 @@ -68,7 +68,7 @@ LL + #[sanitize(hwaddress = "on|off")] LL - #[sanitize(address = "bogus")] LL + #[sanitize(kcfi = "on|off")] | - = and 5 other candidates + = and 6 other candidates error[E0539]: malformed `sanitize` attribute input --> $DIR/invalid-sanitize.rs:17:1 @@ -90,7 +90,7 @@ LL + #[sanitize(hwaddress = "on|off")] LL - #[sanitize = "off"] LL + #[sanitize(kcfi = "on|off")] | - = and 5 other candidates + = and 6 other candidates error[E0539]: malformed `sanitize` attribute input --> $DIR/invalid-sanitize.rs:20:1 @@ -108,8 +108,32 @@ LL | #[sanitize(hwaddress = "on|off")] | ++++++++++++++++++++++ LL | #[sanitize(kcfi = "on|off")] | +++++++++++++++++ - = and 5 other candidates + = and 6 other candidates -error: aborting due to 6 previous errors +error[E0539]: malformed `sanitize` attribute input + --> $DIR/invalid-sanitize.rs:23:1 + | +LL | #[sanitize(realtime = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^----^^ + | | + | valid arguments are "nonblocking", "blocking" or "caller" + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[sanitize(realtime = "on")] +LL + #[sanitize(address = "on|off")] + | +LL - #[sanitize(realtime = "on")] +LL + #[sanitize(cfi = "on|off")] + | +LL - #[sanitize(realtime = "on")] +LL + #[sanitize(hwaddress = "on|off")] + | +LL - #[sanitize(realtime = "on")] +LL + #[sanitize(kcfi = "on|off")] + | + = and 6 other candidates + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/sanitizer/realtime-alloc.rs b/tests/ui/sanitizer/realtime-alloc.rs new file mode 100644 index 0000000000000..06623b05b7837 --- /dev/null +++ b/tests/ui/sanitizer/realtime-alloc.rs @@ -0,0 +1,19 @@ +//@ needs-sanitizer-support +//@ needs-sanitizer-realtime +// +//@ compile-flags: -Z sanitizer=realtime +// +//@ run-crash +//@ error-pattern: Intercepted call to real-time unsafe function `malloc` in real-time context! +//@ ignore-backends: gcc +#![feature(sanitize)] + +#[sanitize(realtime = "nonblocking")] +fn sanitizer_on() { + let mut vec = vec![0, 1, 2]; + println!("alloc not detected"); +} + +fn main() { + sanitizer_on(); +} diff --git a/tests/ui/sanitizer/realtime-blocking.rs b/tests/ui/sanitizer/realtime-blocking.rs new file mode 100644 index 0000000000000..4da80a4dc4242 --- /dev/null +++ b/tests/ui/sanitizer/realtime-blocking.rs @@ -0,0 +1,24 @@ +//@ needs-sanitizer-support +//@ needs-sanitizer-realtime +// +//@ compile-flags: -Z sanitizer=realtime +// +//@ run-crash +//@ error-pattern: Call to blocking function +//@ error-pattern: realtime_blocking::blocking:: +//@ ignore-backends: gcc +#![feature(sanitize)] + +#[sanitize(realtime = "nonblocking")] +fn sanitizer_on() { + blocking(); +} + +#[sanitize(realtime = "blocking")] +fn blocking() { + println!("blocking call not detected"); +} + +fn main() { + sanitizer_on(); +} diff --git a/tests/ui/sanitizer/realtime-caller.rs b/tests/ui/sanitizer/realtime-caller.rs new file mode 100644 index 0000000000000..a4f2176e85a9e --- /dev/null +++ b/tests/ui/sanitizer/realtime-caller.rs @@ -0,0 +1,27 @@ +//@ needs-sanitizer-support +//@ needs-sanitizer-realtime +// +//@ compile-flags: -Z sanitizer=realtime +// +//@ run-crash +//@ error-pattern: RealtimeSanitizer: blocking-call +//@ ignore-backends: gcc +#![feature(sanitize)] + +#[sanitize(realtime = "nonblocking")] +fn sanitizer_on() { + caller(); +} + +fn caller() { + blocking() +} + +#[sanitize(realtime = "blocking")] +fn blocking() { + println!("blocking call not detected"); +} + +fn main() { + sanitizer_on(); +} From 25afc893764f1532726d71cdba41b737136a8b71 Mon Sep 17 00:00:00 2001 From: Lucas Baumann Date: Tue, 26 Aug 2025 20:58:53 +0200 Subject: [PATCH 3/4] add warning for async --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 22 ++++++++++ compiler/rustc_lint_defs/src/builtin.rs | 30 ++++++++++++++ tests/ui/sanitize-attr/invalid-sanitize.rs | 16 ++++++++ .../ui/sanitize-attr/invalid-sanitize.stderr | 40 ++++++++++++++----- 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index c80b1b2797e73..da028f198842a 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -470,6 +470,28 @@ fn check_result( }) } + // warn for nonblocking async fn. + // This doesn't behave as expected, because the executor can run blocking code without the sanitizer noticing. + if codegen_fn_attrs.sanitizers.rtsan_setting == RtsanSetting::Nonblocking + && let Some(sanitize_span) = interesting_spans.sanitize + // async function + && (tcx.asyncness(did).is_async() || (tcx.is_closure_like(did.into()) + // async block + && (tcx.coroutine_is_async(did.into()) + // async closure + || tcx.coroutine_is_async(tcx.coroutine_for_closure(did))))) + { + let hir_id = tcx.local_def_id_to_hir_id(did); + tcx.node_span_lint( + lint::builtin::RTSAN_NONBLOCKING_ASYNC, + hir_id, + sanitize_span, + |lint| { + lint.primary_message(r#"the async executor can run blocking code, without realtime sanitizer catching it"#); + } + ); + } + // error when specifying link_name together with link_ordinal if let Some(_) = codegen_fn_attrs.symbol_name && let Some(_) = codegen_fn_attrs.link_ordinal diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 1e965083d6699..634c91d53dfc4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -87,6 +87,7 @@ declare_lint_pass! { REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + RTSAN_NONBLOCKING_ASYNC, RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, RUST_2021_INCOMPATIBLE_OR_PATTERNS, RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, @@ -2332,6 +2333,35 @@ declare_lint! { r#"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"#, } +declare_lint! { + /// The `rtsan_nonblocking_async` lint detects incompatible use of + /// [`#[sanitize(realtime = "nonblocking")]`][sanitize] on async functions. + /// + /// [sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html + /// ### Example + /// + /// ```rust,no_run + /// #![feature(sanitize)] + /// + /// #[sanitize(realtime = "nonblocking")] + /// async fn x() {} + /// + /// fn main() { + /// x(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The sanitizer only considers the async function body nonblocking. The executor, which runs on + /// every `.await` point can run non-realtime code, without the sanitizer catching it. + pub RTSAN_NONBLOCKING_ASYNC, + Warn, + r#"detects incompatible uses of `#[sanitize(realtime = "nonblocking")]` on async functions"#, +} + declare_lint! { /// The `asm_sub_register` lint detects using only a subset of a register /// for inline asm inputs. diff --git a/tests/ui/sanitize-attr/invalid-sanitize.rs b/tests/ui/sanitize-attr/invalid-sanitize.rs index 707e1a3be7777..63eef51664845 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.rs +++ b/tests/ui/sanitize-attr/invalid-sanitize.rs @@ -1,3 +1,4 @@ +//@ edition:2018 #![feature(sanitize)] #[sanitize(brontosaurus = "off")] //~ ERROR malformed `sanitize` attribute input @@ -22,3 +23,18 @@ fn just_word() {} #[sanitize(realtime = "on")] //~ ERROR malformed `sanitize` attribute input fn wrong_value_realtime() {} + +#[sanitize(realtime = "nonblocking")] //~ WARN: the async executor can run blocking code, without realtime sanitizer catching it [rtsan_nonblocking_async] +async fn async_nonblocking() {} + +fn test() { + let _async_block = { + #[sanitize(realtime = "nonblocking")] //~ WARN: the async executor can run blocking code, without realtime sanitizer catching it [rtsan_nonblocking_async] + async {} + }; + + let _async_closure = { + #[sanitize(realtime = "nonblocking")] //~ WARN: the async executor can run blocking code, without realtime sanitizer catching it [rtsan_nonblocking_async] + async || {} + }; +} diff --git a/tests/ui/sanitize-attr/invalid-sanitize.stderr b/tests/ui/sanitize-attr/invalid-sanitize.stderr index 08aa5257f3f09..9c1a6e5c45285 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.stderr +++ b/tests/ui/sanitize-attr/invalid-sanitize.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `sanitize` attribute input - --> $DIR/invalid-sanitize.rs:3:1 + --> $DIR/invalid-sanitize.rs:4:1 | LL | #[sanitize(brontosaurus = "off")] | ^^^^^^^^^^^------------^^^^^^^^^^ @@ -23,31 +23,31 @@ LL + #[sanitize(kcfi = "on|off")] = and 6 other candidates error: multiple `sanitize` attributes - --> $DIR/invalid-sanitize.rs:6:1 + --> $DIR/invalid-sanitize.rs:7:1 | LL | #[sanitize(address = "off")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-sanitize.rs:7:1 + --> $DIR/invalid-sanitize.rs:8:1 | LL | #[sanitize(address = "off")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: multiple `sanitize` attributes - --> $DIR/invalid-sanitize.rs:10:1 + --> $DIR/invalid-sanitize.rs:11:1 | LL | #[sanitize(address = "on")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-sanitize.rs:11:1 + --> $DIR/invalid-sanitize.rs:12:1 | LL | #[sanitize(address = "off")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0539]: malformed `sanitize` attribute input - --> $DIR/invalid-sanitize.rs:14:1 + --> $DIR/invalid-sanitize.rs:15:1 | LL | #[sanitize(address = "bogus")] | ^^^^^^^^^^^^^^^^^^^^^-------^^ @@ -71,7 +71,7 @@ LL + #[sanitize(kcfi = "on|off")] = and 6 other candidates error[E0539]: malformed `sanitize` attribute input - --> $DIR/invalid-sanitize.rs:17:1 + --> $DIR/invalid-sanitize.rs:18:1 | LL | #[sanitize = "off"] | ^^^^^^^^^^^^^^^^^^^ expected this to be a list @@ -93,7 +93,7 @@ LL + #[sanitize(kcfi = "on|off")] = and 6 other candidates error[E0539]: malformed `sanitize` attribute input - --> $DIR/invalid-sanitize.rs:20:1 + --> $DIR/invalid-sanitize.rs:21:1 | LL | #[sanitize] | ^^^^^^^^^^^ expected this to be a list @@ -111,7 +111,7 @@ LL | #[sanitize(kcfi = "on|off")] = and 6 other candidates error[E0539]: malformed `sanitize` attribute input - --> $DIR/invalid-sanitize.rs:23:1 + --> $DIR/invalid-sanitize.rs:24:1 | LL | #[sanitize(realtime = "on")] | ^^^^^^^^^^^^^^^^^^^^^^----^^ @@ -134,6 +134,26 @@ LL + #[sanitize(kcfi = "on|off")] | = and 6 other candidates -error: aborting due to 7 previous errors +warning: the async executor can run blocking code, without realtime sanitizer catching it + --> $DIR/invalid-sanitize.rs:27:1 + | +LL | #[sanitize(realtime = "nonblocking")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(rtsan_nonblocking_async)]` on by default + +warning: the async executor can run blocking code, without realtime sanitizer catching it + --> $DIR/invalid-sanitize.rs:32:9 + | +LL | ... #[sanitize(realtime = "nonblocking")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the async executor can run blocking code, without realtime sanitizer catching it + --> $DIR/invalid-sanitize.rs:37:9 + | +LL | ... #[sanitize(realtime = "nonblocking")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors; 3 warnings emitted For more information about this error, try `rustc --explain E0539`. From 0573b225a08513edf60ea3e778950d4e5d1e8478 Mon Sep 17 00:00:00 2001 From: Lucas Baumann Date: Sat, 26 Jul 2025 13:42:35 +0200 Subject: [PATCH 4/4] add docs --- .../src/compiler-flags/sanitizer.md | 78 +++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 493256de99d52..f32013d75e500 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -24,6 +24,8 @@ This feature allows for use of one of following sanitizers: AddressSanitizer, but based on partial hardware assistance. * [LeakSanitizer](#leaksanitizer) a run-time memory leak detector. * [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads. + * [RealtimeSanitizer](#realtimesanitizer) a detector of calls to function with + non-deterministic execution time in realtime contexts. * [ThreadSanitizer](#threadsanitizer) a fast data race detector. * Those that apart from testing, may be used in production: @@ -43,11 +45,11 @@ This feature allows for use of one of following sanitizers: To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=cfi`, `-Zsanitizer=dataflow`,`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, -`-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or -`-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. -If you're working with other languages that are also instrumented with sanitizers, -you might need the `external-clangrt` flag. See the section on -[working with other languages](#working-with-other-languages). +`-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=realtime`, +`-Zsanitizer=shadow-call-stack` or `-Zsanitizer=thread`. You might also need the +`--target` and `build-std` flags. If you're working with other languages that are also +instrumented with sanitizers, you might need the `external-clangrt` flag. See +the section on [working with other languages](#working-with-other-languages). Example: ```shell @@ -865,6 +867,70 @@ WARNING: ThreadSanitizer: data race (pid=10574) Location is global 'example::A::h43ac149ddf992709' of size 8 at 0x5632dfe3d030 (example+0x000000bd9030) ``` +# RealtimeSanitizer +RealtimeSanitizer detects non-deterministic execution time calls in real-time contexts. +Functions marked with the `#[sanitize(realtime = "nonblocking")]` attribute are considered real-time functions. +When RTSan detects a call to a function with a non-deterministic execution time, like `malloc` or `free` +while in a real-time context, it reports an error. + +Besides "nonblocking" the attribute can also be used with "blocking" and "caller". +- "blocking" allows the programmer to mark their own functions as having a non-deterministic execution time. +When reaching such a function while in a real-time context a violation will be reported. A typical use +case is a userland spinlock. +- functions marked with "caller" will be sanitized if they were called from a real-time context. +If no attribute is set, this is the default. Between entering a "nonblocking" function and exiting that +function again the program will get sanitized. + +The santizer checks can be disabled using the external functions `__rtsan_disable()` and `__rtsan_enable()`. +Each call to `__rtsan_disable()` must be paired with one following call to `__rtsan_enable()`, otherwise the behaviour is undefined. + +```rust +unsafe extern "C" { + fn __rtsan_disable(); + fn __rtsan_enable(); +} +``` + +```rust,ignore (log is just a example and doesn't exist) +// in a real-time context +#[cfg(debug_assertions)] +{ + unsafe { __rtsan_disable() }; + log!("logging xyz"); + unsafe { __rtsan_enable() }; +} +``` + +See the [Clang RealtimeSanitizer documentation][clang-rtsan] for more details. + +## Example + +```rust,no_run +#![feature(sanitize)] +#[sanitize(realtime = "nonblocking")] +fn real_time() { + let vec = vec![0, 1, 2]; // call to malloc is detected and reported as an error +} +``` + +```shell +==8670==ERROR: RealtimeSanitizer: unsafe-library-call +Intercepted call to real-time unsafe function `malloc` in real-time context! + #0 0x00010107b0d8 in malloc rtsan_interceptors_posix.cpp:792 + #1 0x000100d94e70 in alloc::alloc::Global::alloc_impl::h9e1fc3206c868eea+0xa0 (realtime_vec:arm64+0x100000e70) + #2 0x000100d94d90 in alloc::alloc::exchange_malloc::hd45b5788339eb5c8+0x48 (realtime_vec:arm64+0x100000d90) + #3 0x000100d95020 in realtime_vec::main::hea6bd69b03eb9ca1+0x24 (realtime_vec:arm64+0x100001020) + #4 0x000100d94a28 in core::ops::function::FnOnce::call_once::h493b6cb9dd87d87c+0xc (realtime_vec:arm64+0x100000a28) + #5 0x000100d949b8 in std::sys::backtrace::__rust_begin_short_backtrace::hfcddb06c73c19eea+0x8 (realtime_vec:arm64+0x1000009b8) + #6 0x000100d9499c in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h202288c05a2064f0+0xc (realtime_vec:arm64+0x10000099c) + #7 0x000100d9fa34 in std::rt::lang_start_internal::h6c763158a05ac05f+0x6c (realtime_vec:arm64+0x10000ba34) + #8 0x000100d94980 in std::rt::lang_start::h1c29cc56df0598b4+0x38 (realtime_vec:arm64+0x100000980) + #9 0x000100d95118 in main+0x20 (realtime_vec:arm64+0x100001118) + #10 0x000183a46b94 in start+0x17b8 (dyld:arm64+0xfffffffffff3ab94) + +SUMMARY: RealtimeSanitizer: unsafe-library-call rtsan_interceptors_posix.cpp:792 in malloc +``` + # Instrumentation of external dependencies and std The sanitizers to varying degrees work correctly with partially instrumented @@ -918,6 +984,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT * [MemorySanitizer in Clang][clang-msan] * [MemTagSanitizer in LLVM][llvm-memtag] * [ThreadSanitizer in Clang][clang-tsan] +* [RealtimeSanitizer in Clang][clang-rtsan] [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html [clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html @@ -926,6 +993,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT [clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html +[clan-rtsan]: https://clang.llvm.org/docs/RealtimeSanitizer.html [clang-safestack]: https://clang.llvm.org/docs/SafeStack.html [clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html