Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Commit 87f9dcd

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 bbb6f68 + 9d671a8 commit 87f9dcd

44 files changed

Lines changed: 459 additions & 85 deletions

File tree

Some content is hidden

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

‎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
usesuper::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
constATTRIBUTE_ORDER:AttributeOrder = AttributeOrder::KeepOutermost;
@@ -606,6 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
606607

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

610612
for item in list.mixed(){
611613
letSome(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,14 +679,15 @@ 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
}
679693

‎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)fnsanitize_attrs<'ll,'tcx>(
9999
cx:&SimpleCx<'ll>,
100100
tcx:TyCtxt<'tcx>,
101-
no_sanitize:SanitizerSet,
101+
sanitizer_fn_attr:SanitizerFnAttrs,
102102
) -> SmallVec<[&'llAttribute;4]>{
103103
letmut attrs = SmallVec::new();
104-
let enabled = tcx.sess.sanitizers() - no_sanitize;
104+
let enabled = tcx.sess.sanitizers() - 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.sanitizers().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

@@ -411,7 +423,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
411423
// not used.
412424
}else{
413425
// Do not set sanitizer attributes for naked functions.
414-
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize));
426+
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
415427

416428
// For non-naked functions, set branch protection attributes on aarch64.
417429
ifletSome(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
@@ -633,6 +633,7 @@ pub(crate) unsafe fn llvm_optimize(
633633
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
634634
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
635635
sanitize_memory_track_origins: config.sanitizer_memory_track_originsasc_int,
636+
sanitize_realtime: config.sanitizer.contains(SanitizerSet::REALTIME),
636637
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
637638
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
638639
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
ifletSome(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)fnset_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
@@ -1798,7 +1798,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17981798
&& is_indirect_call
17991799
{
18001800
ifletSome(fn_attrs) = fn_attrs
1801-
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
1801+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::CFI)
18021802
{
18031803
return;
18041804
}
@@ -1856,7 +1856,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18561856
&& is_indirect_call
18571857
{
18581858
ifletSome(fn_attrs) = fn_attrs
1859-
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
1859+
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
18601860
{
18611861
returnNone;
18621862
}

‎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
pubsanitize_memory:bool,
483485
pubsanitize_memory_recover:bool,
484486
pubsanitize_memory_track_origins:c_int,
487+
pubsanitize_realtime:bool,
485488
pubsanitize_thread:bool,
486489
pubsanitize_hwaddress:bool,
487490
pubsanitize_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
@@ -1252,6 +1252,9 @@ fn add_sanitizer_libraries(
12521252
if sanitizer.contains(SanitizerSet::SAFESTACK){
12531253
link_sanitizer_runtime(sess, flavor, linker,"safestack");
12541254
}
1255+
if sanitizer.contains(SanitizerSet::REALTIME){
1256+
link_sanitizer_runtime(sess, flavor, linker,"rtsan");
1257+
}
12551258
}
12561259

12571260
fnlink_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::{selfas 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::{selfas 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
usecrate::errors;
2221
usecrate::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+
&& letSome(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
ifletSome(_) = codegen_fn_attrs.symbol_name
472495
&& letSome(_) = 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-
fndisabled_sanitizers_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerSet{
602+
fnsanitizer_settings_for(tcx:TyCtxt<'_>,did:LocalDefId) -> SanitizerFnAttrs{
580603
// Backtrack to the crate root.
581-
letmutdisabled = match tcx.opt_local_parent(did){
604+
letmutsettings = 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-
ifletSome((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did),AttributeKind::Sanitize{on_set, off_set, ..} => (on_set, off_set))
613+
ifletSome((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+
ifletSome(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
pubpath:Symbol,
383383
}
384384

385+
#[derive(Clone,Copy,Debug,Decodable,Encodable,Eq,PartialEq)]
386+
#[derive(HashStable_Generic,PrintAttribute)]
387+
#[derive_const(Default)]
388+
pubenumRtsanSetting{
389+
Nonblocking,
390+
Blocking,
391+
#[default]
392+
Caller,
393+
}
394+
385395
/// Represents parsed *built-in* inert attributes.
386396
///
387397
/// ## Overview
@@ -689,7 +699,13 @@ pub enum AttributeKind {
689699
///
690700
/// the on set and off set are distjoint since there's a third option: unset.
691701
/// a node may not set the sanitizer setting in which case it inherits from parents.
692-
Sanitize{on_set:SanitizerSet,off_set:SanitizerSet,span:Span},
702+
/// rtsan is unset if None
703+
Sanitize{
704+
on_set:SanitizerSet,
705+
off_set:SanitizerSet,
706+
rtsan:Option<RtsanSetting>,
707+
span:Span,
708+
},
693709

694710
/// Represents `#[should_panic]`
695711
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
@@ -6,6 +6,9 @@
66
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
77
#![feature(associated_type_defaults)]
88
#![feature(closure_track_caller)]
9+
#![feature(const_default)]
10+
#![feature(const_trait_impl)]
11+
#![feature(derive_const)]
912
#![feature(exhaustive_patterns)]
1013
#![feature(never_type)]
1114
#![feature(variant_count)]

0 commit comments

Comments
 (0)