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:
commit
87f9dcd5e2
44 changed files with 459 additions and 85 deletions
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }) =
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}")),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ pub enum Sanitizer {
|
|||
ShadowCallStack,
|
||||
Thread,
|
||||
Hwaddress,
|
||||
Realtime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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 || {}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
20
tests/ui/sanitizer/realtime-alloc.rs
Normal file
20
tests/ui/sanitizer/realtime-alloc.rs
Normal 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();
|
||||
}
|
||||
25
tests/ui/sanitizer/realtime-blocking.rs
Normal file
25
tests/ui/sanitizer/realtime-blocking.rs
Normal 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();
|
||||
}
|
||||
28
tests/ui/sanitizer/realtime-caller.rs
Normal file
28
tests/ui/sanitizer/realtime-caller.rs
Normal 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();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue