Auto merge of #147935 - luca3s:add-rtsan, r=petrochenkov

Add LLVM realtime sanitizer

This is a new attempt at adding the [LLVM real-time sanitizer](https://clang.llvm.org/docs/RealtimeSanitizer.html) to rust.

Previously this was attempted in https://github.com/rust-lang/rfcs/pull/3766.

Since then the `sanitize` attribute was introduced in https://github.com/rust-lang/rust/pull/142681 and it is a lot more flexible than the old `no_santize` attribute. This allows adding real-time sanitizer without the need for a new attribute, like it was proposed in the RFC. Because i only add a new value to a existing command line flag and to a attribute i don't think an MCP is necessary.

Currently real-time santizer is usable in rust code with the [rtsan-standalone](https://crates.io/crates/rtsan-standalone) crate. This downloads or builds the sanitizer runtime and then links it into the rust binary.

The first commit adds support for more detailed sanitizer information.
The second commit then actually adds real-time sanitizer.
The third adds a warning against using real-time sanitizer with async functions, cloures and blocks because it doesn't behave as expected when used with async functions. I am not sure if this is actually wanted, so i kept it in a seperate commit.
The fourth commit adds the documentation for real-time sanitizer.
This commit is contained in:
bors 2025-11-08 12:24:15 +00:00
commit 87f9dcd5e2
44 changed files with 459 additions and 85 deletions

View file

@ -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<S: Stage> SingleAttributeParser<S> 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<S: Stage> SingleAttributeParser<S> 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<S: Stage> SingleAttributeParser<S> 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<S: Stage> SingleAttributeParser<S> for SanitizeParser {
sym::shadow_call_stack,
sym::thread,
sym::hwaddress,
sym::realtime,
],
);
continue;
@ -673,7 +687,7 @@ impl<S: Stage> SingleAttributeParser<S> 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 })
}
}

View file

@ -1,8 +1,8 @@
//! 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,
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
};
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
@ -98,10 +98,10 @@ 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.sanitizers() - no_sanitize;
let enabled = tcx.sess.sanitizers() - sanitizer_fn_attr.disabled;
if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
}
@ -131,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.sanitizers().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
}
@ -411,7 +423,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 }) =

View file

@ -633,6 +633,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),

View file

@ -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::<Builder<'_, '_, '_>>(&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) };
}
}

View file

@ -1798,7 +1798,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;
}
@ -1856,7 +1856,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;
}

View file

@ -290,6 +290,8 @@ pub(crate) enum AttributeKind {
DeadOnReturn = 44,
CapturesReadOnly = 45,
CapturesNone = 46,
SanitizeRealtimeNonblocking = 47,
SanitizeRealtimeBlocking = 48,
}
/// LLVMIntPredicate
@ -482,6 +484,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,

View file

@ -1252,6 +1252,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(

View file

@ -3,12 +3,12 @@ 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};
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::{
@ -350,8 +349,10 @@ 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.no_sanitize |= tcx.disabled_sanitizers_for(did);
// Passed in sanitizer settings are always the default.
assert!(codegen_fn_attrs.sanitizers == SanitizerFnAttrs::default());
// Replace with #[sanitize] value
codegen_fn_attrs.sanitizers = tcx.sanitizer_settings_for(did);
// 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,18 +456,40 @@ 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");
})
}
// 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
@ -576,30 +599,35 @@ 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.
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
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
// if rtsan was specified here override the parent
if let Some(rtsan) = rtsan {
settings.rtsan_setting = *rtsan;
}
}
disabled
settings
}
/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
@ -731,7 +759,7 @@ pub(crate) fn provide(providers: &mut Providers) {
codegen_fn_attrs,
should_inherit_track_caller,
inherited_align,
disabled_sanitizers_for,
sanitizer_settings_for,
..*providers
};
}

View file

@ -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
@ -689,7 +699,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<RtsanSetting>,
span: Span,
},
/// Represents `#[should_panic]`
ShouldPanic { reason: Option<Symbol>, span: Span },

View file

@ -6,6 +6,9 @@
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
#![feature(associated_type_defaults)]
#![feature(closure_track_caller)]
#![feature(const_default)]
#![feature(const_trait_impl)]
#![feature(derive_const)]
#![feature(exhaustive_patterns)]
#![feature(never_type)]
#![feature(variant_count)]

View file

@ -88,6 +88,7 @@ declare_lint_pass! {
RENAMED_AND_REMOVED_LINTS,
REPR_C_ENUMS_LARGER_THAN_INT,
REPR_TRANSPARENT_NON_ZST_FIELDS,
RTSAN_NONBLOCKING_ASYNC,
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
@ -2333,6 +2334,37 @@ 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
///
#[cfg_attr(bootstrap, doc = "```ignore")]
#[cfg_attr(not(bootstrap), doc = "```rust,no_run")]
/// #![feature(sanitize)]
///
/// #[sanitize(realtime = "nonblocking")]
/// async fn x() {}
///
/// fn main() {
/// x();
/// }
#[cfg_attr(bootstrap, doc = "```")]
#[cfg_attr(not(bootstrap), doc = "```")]
///
/// {{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.

View file

@ -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"
@ -531,6 +532,7 @@ struct LLVMRustSanitizerOptions {
bool SanitizeMemory;
bool SanitizeMemoryRecover;
int SanitizeMemoryTrackOrigins;
bool SanitizerRealtime;
bool SanitizeThread;
bool SanitizeHWAddress;
bool SanitizeHWAddressRecover;
@ -786,6 +788,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;

View file

@ -270,6 +270,8 @@ enum class LLVMRustAttributeKind {
DeadOnReturn = 44,
CapturesReadOnly = 45,
CapturesNone = 46,
SanitizeRealtimeNonblocking = 47,
SanitizeRealtimeBlocking = 48,
};
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
@ -366,6 +368,10 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
case LLVMRustAttributeKind::CapturesReadOnly:
case LLVMRustAttributeKind::CapturesNone:
report_fatal_error("Should be handled separately");
case LLVMRustAttributeKind::SanitizeRealtimeNonblocking:
return Attribute::SanitizeRealtime;
case LLVMRustAttributeKind::SanitizeRealtimeBlocking:
return Attribute::SanitizeRealtimeBlocking;
}
report_fatal_error("bad LLVMRustAttributeKind");
}

View file

@ -36,6 +36,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)]

View file

@ -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;
@ -80,9 +80,9 @@ pub struct CodegenFnAttrs {
/// The `#[link_section = "..."]` attribute, or what executable section this
/// should be placed in.
pub link_section: Option<Symbol>,
/// 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,
pub rtsan_setting: RtsanSetting,
}
impl const Default for SanitizerFnAttrs {
fn default() -> Self {
Self { disabled: SanitizerSet::empty(), rtsan_setting: RtsanSetting::default() }
}
}

View file

@ -348,6 +348,7 @@ trivial! {
rustc_middle::ty::UnusedGenericParams,
rustc_middle::ty::util::AlwaysRequiresDrop,
rustc_middle::ty::Visibility<rustc_span::def_id::DefId>,
rustc_middle::middle::codegen_fn_attrs::SanitizerFnAttrs,
rustc_session::config::CrateType,
rustc_session::config::EntryFnType,
rustc_session::config::OptLevel,
@ -365,7 +366,6 @@ trivial! {
rustc_span::Symbol,
rustc_span::Ident,
rustc_target::spec::PanicStrategy,
rustc_target::spec::SanitizerSet,
rustc_type_ir::Variance,
u32,
usize,

View file

@ -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};
@ -2735,8 +2735,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
}

View file

@ -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");
}

View file

@ -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)) => {

View file

@ -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`";
@ -1278,6 +1278,7 @@ pub mod parse {
"thread" => SanitizerSet::THREAD,
"hwaddress" => SanitizerSet::HWADDRESS,
"safestack" => SanitizerSet::SAFESTACK,
"realtime" => SanitizerSet::REALTIME,
_ => return false,
}
}

View file

@ -583,6 +583,7 @@ symbols! {
bitxor_assign,
black_box,
block,
blocking,
bool,
bool_then,
borrowck_graphviz_format,
@ -616,6 +617,7 @@ symbols! {
call_once,
call_once_future,
call_ref_future,
caller,
caller_location,
capture_disjoint_fields,
carrying_mul_add,
@ -1563,6 +1565,7 @@ symbols! {
non_exhaustive_omitted_patterns_lint,
non_lifetime_binders,
non_modrs_mods,
nonblocking,
none,
nontemporal_store,
noop_method_borrow,
@ -1803,6 +1806,7 @@ symbols! {
read_via_copy,
readonly,
realloc,
realtime,
reason,
reborrow,
receiver,

View file

@ -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}")),
})
}

View file

@ -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
},

View file

@ -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
},
}

View file

@ -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
},
}

View file

@ -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()
},

View file

@ -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
},

View file

@ -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 {

View file

@ -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 hasnt 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

View file

@ -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"])
}

View file

@ -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|<other>")]`.
* The decision whether to perform instrumentation or not is possible only at a
function granularity. In the cases were those decision differ between

View file

@ -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

View file

@ -174,6 +174,7 @@ pub enum Sanitizer {
ShadowCallStack,
Thread,
Hwaddress,
Realtime,
}
#[derive(Clone, Copy, Debug, PartialEq)]

View file

@ -165,6 +165,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",

View file

@ -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,

View file

@ -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

View file

@ -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 <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`

View file

@ -1,3 +1,4 @@
//@ edition:2018
#![feature(sanitize)]
#[sanitize(brontosaurus = "off")] //~ ERROR malformed `sanitize` attribute input
@ -19,3 +20,21 @@ fn name_value() {}
#[sanitize] //~ ERROR malformed `sanitize` attribute input
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 || {}
};
}

View file

@ -1,10 +1,10 @@
error[E0539]: malformed `sanitize` attribute input
--> $DIR/invalid-sanitize.rs:3:1
--> $DIR/invalid-sanitize.rs:4:1
|
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,34 +20,34 @@ 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
--> $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")]
| ^^^^^^^^^^^^^^^^^^^^^-------^^
@ -68,10 +68,10 @@ 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
--> $DIR/invalid-sanitize.rs:18:1
|
LL | #[sanitize = "off"]
| ^^^^^^^^^^^^^^^^^^^ expected this to be a list
@ -90,10 +90,10 @@ 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
--> $DIR/invalid-sanitize.rs:21:1
|
LL | #[sanitize]
| ^^^^^^^^^^^ expected this to be a list
@ -108,8 +108,52 @@ 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:24: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
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`.

View file

@ -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() {
}

View file

@ -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")]

View file

@ -0,0 +1,20 @@
//@ needs-sanitizer-support
//@ needs-sanitizer-realtime
//
//@ compile-flags: -Z sanitizer=realtime
//@ exec-env: RTSAN_OPTIONS=abort_on_error=0
//
//@ run-fail
//@ 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();
}

View file

@ -0,0 +1,25 @@
//@ needs-sanitizer-support
//@ needs-sanitizer-realtime
//
//@ compile-flags: -Z sanitizer=realtime
//@ exec-env: RTSAN_OPTIONS=abort_on_error=0
//
//@ run-fail
//@ 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();
}

View file

@ -0,0 +1,28 @@
//@ needs-sanitizer-support
//@ needs-sanitizer-realtime
//
//@ compile-flags: -Z sanitizer=realtime
//@ exec-env: RTSAN_OPTIONS=abort_on_error=0
//
//@ run-fail
//@ 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();
}