Skip to content

Commit 93cd52d

Browse files
committed
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 rust-lang/rfcs#3766. Since then the `sanitize` attribute was introduced in #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.
2 parents 292be5c + 2e1fdb4 commit 93cd52d

File tree

44 files changed

+457
-85
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+457
-85
lines changed

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
1+
use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy};
22
use rustc_session::parse::feature_err;
33

44
use super::prelude::*;
@@ -592,7 +592,8 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
592592
r#"memory = "on|off""#,
593593
r#"memtag = "on|off""#,
594594
r#"shadow_call_stack = "on|off""#,
595-
r#"thread = "on|off""#
595+
r#"thread = "on|off""#,
596+
r#"realtime = "nonblocking|blocking|caller""#,
596597
]);
597598

598599
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

607608
let mut on_set = SanitizerSet::empty();
608609
let mut off_set = SanitizerSet::empty();
610+
let mut rtsan = None;
609611

610612
for item in list.mixed() {
611613
let Some(item) = item.meta_item() else {
@@ -654,6 +656,17 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
654656
Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
655657
Some(sym::thread) => apply(SanitizerSet::THREAD),
656658
Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
659+
Some(sym::realtime) => match value.value_as_str() {
660+
Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
661+
Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
662+
Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
663+
_ => {
664+
cx.expected_specific_argument_strings(
665+
value.value_span,
666+
&[sym::nonblocking, sym::blocking, sym::caller],
667+
);
668+
}
669+
},
657670
_ => {
658671
cx.expected_specific_argument_strings(
659672
item.path().span(),
@@ -666,13 +679,14 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
666679
sym::shadow_call_stack,
667680
sym::thread,
668681
sym::hwaddress,
682+
sym::realtime,
669683
],
670684
);
671685
continue;
672686
}
673687
}
674688
}
675689

676-
Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
690+
Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
677691
}
678692
}

compiler/rustc_codegen_llvm/src/attributes.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Set and unset common attributes on LLVM values.
2-
use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr};
2+
use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr, RtsanSetting};
33
use rustc_hir::def_id::DefId;
44
use rustc_middle::middle::codegen_fn_attrs::{
5-
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
5+
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
66
};
77
use rustc_middle::ty::{self, TyCtxt};
88
use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
@@ -98,10 +98,10 @@ fn patchable_function_entry_attrs<'ll>(
9898
pub(crate) fn sanitize_attrs<'ll, 'tcx>(
9999
cx: &SimpleCx<'ll>,
100100
tcx: TyCtxt<'tcx>,
101-
no_sanitize: SanitizerSet,
101+
sanitizer_fn_attr: SanitizerFnAttrs,
102102
) -> SmallVec<[&'ll Attribute; 4]> {
103103
let mut attrs = SmallVec::new();
104-
let enabled = tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
104+
let enabled = tcx.sess.opts.unstable_opts.sanitizer - sanitizer_fn_attr.disabled;
105105
if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
106106
attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
107107
}
@@ -131,6 +131,18 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>(
131131
if enabled.contains(SanitizerSet::SAFESTACK) {
132132
attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
133133
}
134+
if tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::REALTIME) {
135+
match sanitizer_fn_attr.rtsan_setting {
136+
RtsanSetting::Nonblocking => {
137+
attrs.push(llvm::AttributeKind::SanitizeRealtimeNonblocking.create_attr(cx.llcx))
138+
}
139+
RtsanSetting::Blocking => {
140+
attrs.push(llvm::AttributeKind::SanitizeRealtimeBlocking.create_attr(cx.llcx))
141+
}
142+
// caller is the default, so no llvm attribute
143+
RtsanSetting::Caller => (),
144+
}
145+
}
134146
attrs
135147
}
136148

@@ -417,7 +429,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
417429
// not used.
418430
} else {
419431
// Do not set sanitizer attributes for naked functions.
420-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
432+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
421433

422434
// For non-naked functions, set branch protection attributes on aarch64.
423435
if let Some(BranchProtection { bti, pac_ret, gcs }) =

compiler/rustc_codegen_llvm/src/back/write.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ pub(crate) unsafe fn llvm_optimize(
630630
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
631631
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
632632
sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int,
633+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
633634
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
634635
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
635636
sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS),

compiler/rustc_codegen_llvm/src/base.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_codegen_ssa::traits::*;
2020
use rustc_data_structures::small_c_str::SmallCStr;
2121
use rustc_hir::attrs::Linkage;
2222
use rustc_middle::dep_graph;
23-
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
23+
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs};
2424
use rustc_middle::mir::mono::Visibility;
2525
use rustc_middle::ty::TyCtxt;
2626
use rustc_session::config::DebugInfo;
@@ -105,7 +105,7 @@ pub(crate) fn compile_codegen_unit(
105105
if let Some(entry) =
106106
maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx, cx.codegen_unit)
107107
{
108-
let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerSet::empty());
108+
let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default());
109109
attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs);
110110
}
111111

@@ -191,10 +191,10 @@ pub(crate) fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility {
191191
}
192192

193193
pub(crate) fn set_variable_sanitizer_attrs(llval: &Value, attrs: &CodegenFnAttrs) {
194-
if attrs.no_sanitize.contains(SanitizerSet::ADDRESS) {
194+
if attrs.sanitizers.disabled.contains(SanitizerSet::ADDRESS) {
195195
unsafe { llvm::LLVMRustSetNoSanitizeAddress(llval) };
196196
}
197-
if attrs.no_sanitize.contains(SanitizerSet::HWADDRESS) {
197+
if attrs.sanitizers.disabled.contains(SanitizerSet::HWADDRESS) {
198198
unsafe { llvm::LLVMRustSetNoSanitizeHWAddress(llval) };
199199
}
200200
}

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,7 +1803,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18031803
&& is_indirect_call
18041804
{
18051805
if let Some(fn_attrs) = fn_attrs
1806-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1806+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18071807
{
18081808
return;
18091809
}
@@ -1861,7 +1861,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18611861
&& is_indirect_call
18621862
{
18631863
if let Some(fn_attrs) = fn_attrs
1864-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1864+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18651865
{
18661866
return None;
18671867
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ pub(crate) enum AttributeKind {
290290
DeadOnReturn = 44,
291291
CapturesReadOnly = 45,
292292
CapturesNone = 46,
293+
SanitizeRealtimeNonblocking = 47,
294+
SanitizeRealtimeBlocking = 48,
293295
}
294296

295297
/// LLVMIntPredicate
@@ -482,6 +484,7 @@ pub(crate) struct SanitizerOptions {
482484
pub sanitize_memory: bool,
483485
pub sanitize_memory_recover: bool,
484486
pub sanitize_memory_track_origins: c_int,
487+
pub sanitize_realtime: bool,
485488
pub sanitize_thread: bool,
486489
pub sanitize_hwaddress: bool,
487490
pub sanitize_hwaddress_recover: bool,

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,9 @@ fn add_sanitizer_libraries(
12511251
if sanitizer.contains(SanitizerSet::SAFESTACK) {
12521252
link_sanitizer_runtime(sess, flavor, linker, "safestack");
12531253
}
1254+
if sanitizer.contains(SanitizerSet::REALTIME) {
1255+
link_sanitizer_runtime(sess, flavor, linker, "rtsan");
1256+
}
12541257
}
12551258

12561259
fn link_sanitizer_runtime(

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,19 @@ use std::str::FromStr;
33
use rustc_abi::{Align, ExternAbi};
44
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
55
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
6-
use rustc_hir::attrs::{AttributeKind, InlineAttr, InstructionSetAttr, UsedBy};
6+
use rustc_hir::attrs::{AttributeKind, InlineAttr, InstructionSetAttr, RtsanSetting, UsedBy};
77
use rustc_hir::def::DefKind;
88
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
99
use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items};
1010
use rustc_middle::middle::codegen_fn_attrs::{
11-
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
11+
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
1212
};
1313
use rustc_middle::query::Providers;
1414
use rustc_middle::span_bug;
1515
use rustc_middle::ty::{self as ty, TyCtxt};
1616
use rustc_session::lint;
1717
use rustc_session::parse::feature_err;
1818
use rustc_span::{Ident, Span, sym};
19-
use rustc_target::spec::SanitizerSet;
2019

2120
use crate::errors;
2221
use crate::target_features::{
@@ -350,8 +349,10 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
350349
codegen_fn_attrs.alignment =
351350
Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment);
352351

353-
// Compute the disabled sanitizers.
354-
codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did);
352+
// Passed in sanitizer settings are always the default.
353+
assert!(codegen_fn_attrs.sanitizers == SanitizerFnAttrs::default());
354+
// Replace with #[sanitize] value
355+
codegen_fn_attrs.sanitizers = tcx.sanitizer_settings_for(did);
355356
// On trait methods, inherit the `#[align]` of the trait's method prototype.
356357
codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did));
357358

@@ -455,18 +456,40 @@ fn check_result(
455456
}
456457

457458
// warn that inline has no effect when no_sanitize is present
458-
if !codegen_fn_attrs.no_sanitize.is_empty()
459+
if codegen_fn_attrs.sanitizers != SanitizerFnAttrs::default()
459460
&& codegen_fn_attrs.inline.always()
460-
&& let (Some(no_sanitize_span), Some(inline_span)) =
461+
&& let (Some(sanitize_span), Some(inline_span)) =
461462
(interesting_spans.sanitize, interesting_spans.inline)
462463
{
463464
let hir_id = tcx.local_def_id_to_hir_id(did);
464-
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
465-
lint.primary_message("setting `sanitize` off will have no effect after inlining");
465+
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| {
466+
lint.primary_message("non-default `sanitize` will have no effect after inlining");
466467
lint.span_note(inline_span, "inlining requested here");
467468
})
468469
}
469470

471+
// warn for nonblocking async fn.
472+
// This doesn't behave as expected, because the executor can run blocking code without the sanitizer noticing.
473+
if codegen_fn_attrs.sanitizers.rtsan_setting == RtsanSetting::Nonblocking
474+
&& let Some(sanitize_span) = interesting_spans.sanitize
475+
// async function
476+
&& (tcx.asyncness(did).is_async() || (tcx.is_closure_like(did.into())
477+
// async block
478+
&& (tcx.coroutine_is_async(did.into())
479+
// async closure
480+
|| tcx.coroutine_is_async(tcx.coroutine_for_closure(did)))))
481+
{
482+
let hir_id = tcx.local_def_id_to_hir_id(did);
483+
tcx.node_span_lint(
484+
lint::builtin::RTSAN_NONBLOCKING_ASYNC,
485+
hir_id,
486+
sanitize_span,
487+
|lint| {
488+
lint.primary_message(r#"the async executor can run blocking code, without realtime sanitizer catching it"#);
489+
}
490+
);
491+
}
492+
470493
// error when specifying link_name together with link_ordinal
471494
if let Some(_) = codegen_fn_attrs.symbol_name
472495
&& let Some(_) = codegen_fn_attrs.link_ordinal
@@ -576,30 +599,35 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
576599
codegen_fn_attrs
577600
}
578601

579-
fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet {
602+
fn sanitizer_settings_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerFnAttrs {
580603
// Backtrack to the crate root.
581-
let mut disabled = match tcx.opt_local_parent(did) {
604+
let mut settings = match tcx.opt_local_parent(did) {
582605
// Check the parent (recursively).
583-
Some(parent) => tcx.disabled_sanitizers_for(parent),
606+
Some(parent) => tcx.sanitizer_settings_for(parent),
584607
// We reached the crate root without seeing an attribute, so
585608
// there is no sanitizers to exclude.
586-
None => SanitizerSet::empty(),
609+
None => SanitizerFnAttrs::default(),
587610
};
588611

589612
// Check for a sanitize annotation directly on this def.
590-
if let Some((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did), AttributeKind::Sanitize {on_set, off_set, ..} => (on_set, off_set))
613+
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))
591614
{
592615
// the on set is the set of sanitizers explicitly enabled.
593616
// we mask those out since we want the set of disabled sanitizers here
594-
disabled &= !*on_set;
617+
settings.disabled &= !*on_set;
595618
// the off set is the set of sanitizers explicitly disabled.
596619
// we or those in here.
597-
disabled |= *off_set;
620+
settings.disabled |= *off_set;
598621
// the on set and off set are distjoint since there's a third option: unset.
599622
// a node may not set the sanitizer setting in which case it inherits from parents.
600623
// the code above in this function does this backtracking
624+
625+
// if rtsan was specified here override the parent
626+
if let Some(rtsan) = rtsan {
627+
settings.rtsan_setting = *rtsan;
628+
}
601629
}
602-
disabled
630+
settings
603631
}
604632

605633
/// 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) {
731759
codegen_fn_attrs,
732760
should_inherit_track_caller,
733761
inherited_align,
734-
disabled_sanitizers_for,
762+
sanitizer_settings_for,
735763
..*providers
736764
};
737765
}

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,16 @@ pub struct DebugVisualizer {
382382
pub path: Symbol,
383383
}
384384

385+
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
386+
#[derive(HashStable_Generic, PrintAttribute)]
387+
#[derive_const(Default)]
388+
pub enum RtsanSetting {
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -683,7 +693,13 @@ pub enum AttributeKind {
683693
///
684694
/// the on set and off set are distjoint since there's a third option: unset.
685695
/// a node may not set the sanitizer setting in which case it inherits from parents.
686-
Sanitize { on_set: SanitizerSet, off_set: SanitizerSet, span: Span },
696+
/// rtsan is unset if None
697+
Sanitize {
698+
on_set: SanitizerSet,
699+
off_set: SanitizerSet,
700+
rtsan: Option<RtsanSetting>,
701+
span: Span,
702+
},
687703

688704
/// Represents `#[should_panic]`
689705
ShouldPanic { reason: Option<Symbol>, span: Span },

compiler/rustc_hir/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
// tidy-alphabetical-start
66
#![feature(associated_type_defaults)]
77
#![feature(closure_track_caller)]
8+
#![feature(const_default)]
9+
#![feature(const_trait_impl)]
810
#![feature(debug_closure_helpers)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)