Skip to content

Commit 2f1eaee

Browse files
authored
Rollup merge of #68164 - tmiasko:no-sanitize, r=nikomatsakis
Selectively disable sanitizer instrumentation Add `no_sanitize` attribute that allows to opt out from sanitizer instrumentation in an annotated function.
2 parents b5e21db + 80adde2 commit 2f1eaee

File tree

20 files changed

+279
-29
lines changed

20 files changed

+279
-29
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# `no_sanitize`
2+
3+
The tracking issue for this feature is: [#39699]
4+
5+
[#39699]: https://github.com/rust-lang/rust/issues/39699
6+
7+
------------------------
8+
9+
The `no_sanitize` attribute can be used to selectively disable sanitizer
10+
instrumentation in an annotated function. This might be useful to: avoid
11+
instrumentation overhead in a performance critical function, or avoid
12+
instrumenting code that contains constructs unsupported by given sanitizer.
13+
14+
The precise effect of this annotation depends on particular sanitizer in use.
15+
For example, with `no_sanitize(thread)`, the thread sanitizer will no longer
16+
instrument non-atomic store / load operations, but it will instrument atomic
17+
operations to avoid reporting false positives and provide meaning full stack
18+
traces.
19+
20+
## Examples
21+
22+
``` rust
23+
#![feature(no_sanitize)]
24+
25+
#[no_sanitize(address)]
26+
fn foo() {
27+
// ...
28+
}
29+
```

src/librustc/middle/codegen_fn_attrs.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ bitflags! {
7272
const FFI_RETURNS_TWICE = 1 << 10;
7373
/// `#[track_caller]`: allow access to the caller location
7474
const TRACK_CALLER = 1 << 11;
75+
/// `#[no_sanitize(address)]`: disables address sanitizer instrumentation
76+
const NO_SANITIZE_ADDRESS = 1 << 12;
77+
/// `#[no_sanitize(memory)]`: disables memory sanitizer instrumentation
78+
const NO_SANITIZE_MEMORY = 1 << 13;
79+
/// `#[no_sanitize(thread)]`: disables thread sanitizer instrumentation
80+
const NO_SANITIZE_THREAD = 1 << 14;
81+
/// All `#[no_sanitize(...)]` attributes.
82+
const NO_SANITIZE_ANY = Self::NO_SANITIZE_ADDRESS.bits | Self::NO_SANITIZE_MEMORY.bits | Self::NO_SANITIZE_THREAD.bits;
7583
}
7684
}
7785

src/librustc_codegen_llvm/attributes.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,31 @@ fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
4646
};
4747
}
4848

49+
/// Apply LLVM sanitize attributes.
50+
#[inline]
51+
pub fn sanitize(cx: &CodegenCx<'ll, '_>, codegen_fn_flags: CodegenFnAttrFlags, llfn: &'ll Value) {
52+
if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer {
53+
match *sanitizer {
54+
Sanitizer::Address => {
55+
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) {
56+
llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
57+
}
58+
}
59+
Sanitizer::Memory => {
60+
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) {
61+
llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
62+
}
63+
}
64+
Sanitizer::Thread => {
65+
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) {
66+
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
67+
}
68+
}
69+
Sanitizer::Leak => {}
70+
}
71+
}
72+
}
73+
4974
/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
5075
#[inline]
5176
pub fn emit_uwtable(val: &'ll Value, emit: bool) {
@@ -288,6 +313,7 @@ pub fn from_fn_attrs(
288313
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
289314
Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
290315
}
316+
sanitize(cx, codegen_fn_attrs.flags, llfn);
291317

292318
unwind(
293319
llfn,

src/librustc_codegen_llvm/base.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
1616
use super::ModuleLlvm;
1717

18+
use crate::attributes;
1819
use crate::builder::Builder;
1920
use crate::common;
2021
use crate::context::CodegenCx;
@@ -23,7 +24,7 @@ use crate::metadata;
2324
use crate::value::Value;
2425

2526
use rustc::dep_graph;
26-
use rustc::middle::codegen_fn_attrs::CodegenFnAttrs;
27+
use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
2728
use rustc::middle::cstore::EncodedMetadata;
2829
use rustc::middle::exported_symbols;
2930
use rustc::mir::mono::{Linkage, Visibility};
@@ -131,7 +132,9 @@ pub fn compile_codegen_unit(
131132

132133
// If this codegen unit contains the main function, also create the
133134
// wrapper here
134-
maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx);
135+
if let Some(entry) = maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx) {
136+
attributes::sanitize(&cx, CodegenFnAttrFlags::empty(), entry);
137+
}
135138

136139
// Run replace-all-uses-with for statics that need it
137140
for &(old_g, new_g) in cx.statics_to_rauw().borrow().iter() {

src/librustc_codegen_llvm/declare.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use crate::llvm::AttributePlace::Function;
1919
use crate::type_::Type;
2020
use crate::value::Value;
2121
use log::debug;
22-
use rustc::session::config::Sanitizer;
2322
use rustc::ty::Ty;
2423
use rustc_codegen_ssa::traits::*;
2524
use rustc_data_structures::small_c_str::SmallCStr;
@@ -47,21 +46,6 @@ fn declare_raw_fn(
4746
llvm::Attribute::NoRedZone.apply_llfn(Function, llfn);
4847
}
4948

50-
if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer {
51-
match *sanitizer {
52-
Sanitizer::Address => {
53-
llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
54-
}
55-
Sanitizer::Memory => {
56-
llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
57-
}
58-
Sanitizer::Thread => {
59-
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
60-
}
61-
_ => {}
62-
}
63-
}
64-
6549
attributes::default_optimisation_attrs(cx.tcx.sess, llfn);
6650
attributes::non_lazy_bind(cx.sess(), llfn);
6751
llfn

src/librustc_codegen_ssa/base.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -391,36 +391,36 @@ pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
391391

392392
/// Creates the `main` function which will initialize the rust runtime and call
393393
/// users main function.
394-
pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(cx: &'a Bx::CodegenCx) {
394+
pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
395+
cx: &'a Bx::CodegenCx,
396+
) -> Option<Bx::Function> {
395397
let (main_def_id, span) = match cx.tcx().entry_fn(LOCAL_CRATE) {
396398
Some((def_id, _)) => (def_id, cx.tcx().def_span(def_id)),
397-
None => return,
399+
None => return None,
398400
};
399401

400402
let instance = Instance::mono(cx.tcx(), main_def_id);
401403

402404
if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) {
403405
// We want to create the wrapper in the same codegen unit as Rust's main
404406
// function.
405-
return;
407+
return None;
406408
}
407409

408410
let main_llfn = cx.get_fn_addr(instance);
409411

410-
let et = cx.tcx().entry_fn(LOCAL_CRATE).map(|e| e.1);
411-
match et {
412-
Some(EntryFnType::Main) => create_entry_fn::<Bx>(cx, span, main_llfn, main_def_id, true),
413-
Some(EntryFnType::Start) => create_entry_fn::<Bx>(cx, span, main_llfn, main_def_id, false),
414-
None => {} // Do nothing.
415-
}
412+
return cx.tcx().entry_fn(LOCAL_CRATE).map(|(_, et)| {
413+
let use_start_lang_item = EntryFnType::Start != et;
414+
create_entry_fn::<Bx>(cx, span, main_llfn, main_def_id, use_start_lang_item)
415+
});
416416

417417
fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
418418
cx: &'a Bx::CodegenCx,
419419
sp: Span,
420420
rust_main: Bx::Value,
421421
rust_main_def_id: DefId,
422422
use_start_lang_item: bool,
423-
) {
423+
) -> Bx::Function {
424424
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`,
425425
// depending on whether the target needs `argc` and `argv` to be passed in.
426426
let llfty = if cx.sess().target.target.options.main_needs_argc_argv {
@@ -481,6 +481,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(cx: &'
481481
let result = bx.call(start_fn, &args, None);
482482
let cast = bx.intcast(result, cx.type_int(), true);
483483
bx.ret(cast);
484+
485+
llfn
484486
}
485487
}
486488

src/librustc_feature/active.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,9 @@ declare_features! (
541541
/// Allows `T: ?const Trait` syntax in bounds.
542542
(active, const_trait_bound_opt_out, "1.42.0", Some(67794), None),
543543

544+
/// Allows the use of `no_sanitize` attribute.
545+
(active, no_sanitize, "1.42.0", Some(39699), None),
546+
544547
// -------------------------------------------------------------------------
545548
// feature-group-end: actual feature gates
546549
// -------------------------------------------------------------------------

src/librustc_feature/builtin_attrs.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
261261
ungated!(cold, Whitelisted, template!(Word)),
262262
ungated!(no_builtins, Whitelisted, template!(Word)),
263263
ungated!(target_feature, Whitelisted, template!(List: r#"enable = "name""#)),
264+
gated!(
265+
no_sanitize, Whitelisted,
266+
template!(List: "address, memory, thread"),
267+
experimental!(no_sanitize)
268+
),
264269

265270
// FIXME: #14408 whitelist docs since rustdoc looks at them
266271
ungated!(doc, Whitelisted, template!(List: "hidden|inline|...", NameValueStr: "string")),

src/librustc_mir/transform/inline.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_index::vec::{Idx, IndexVec};
88
use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags;
99
use rustc::mir::visit::*;
1010
use rustc::mir::*;
11+
use rustc::session::config::Sanitizer;
1112
use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef};
1213
use rustc::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
1314

@@ -228,6 +229,28 @@ impl Inliner<'tcx> {
228229
return false;
229230
}
230231

232+
// Avoid inlining functions marked as no_sanitize if sanitizer is enabled,
233+
// since instrumentation might be enabled and performed on the caller.
234+
match self.tcx.sess.opts.debugging_opts.sanitizer {
235+
Some(Sanitizer::Address) => {
236+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) {
237+
return false;
238+
}
239+
}
240+
Some(Sanitizer::Memory) => {
241+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) {
242+
return false;
243+
}
244+
}
245+
Some(Sanitizer::Thread) => {
246+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) {
247+
return false;
248+
}
249+
}
250+
Some(Sanitizer::Leak) => {}
251+
None => {}
252+
}
253+
231254
let hinted = match codegen_fn_attrs.inline {
232255
// Just treat inline(always) as a hint for now,
233256
// there are cases that prevent inlining that we

src/librustc_session/lint/builtin.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,12 @@ declare_lint! {
474474
};
475475
}
476476

477+
declare_lint! {
478+
pub INLINE_NO_SANITIZE,
479+
Warn,
480+
"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`",
481+
}
482+
477483
declare_lint_pass! {
478484
/// Does nothing as a lint pass, but registers some `Lint`s
479485
/// that are used by other parts of the compiler.
@@ -537,5 +543,6 @@ declare_lint_pass! {
537543
MUTABLE_BORROW_RESERVATION_CONFLICT,
538544
INDIRECT_STRUCTURAL_MATCH,
539545
SOFT_UNSTABLE,
546+
INLINE_NO_SANITIZE,
540547
]
541548
}

0 commit comments

Comments
 (0)