|
| 1 | +# `sanitizer` |
| 2 | + |
| 3 | +The tracking issue for this feature is: [#39699](https://github.com/rust-lang/rust/issues/39699). |
| 4 | + |
| 5 | +------------------------ |
| 6 | + |
| 7 | +This feature allows for use of one of following sanitizers: |
| 8 | + |
| 9 | +* [AddressSanitizer][clang-asan] a faster memory error detector. Can |
| 10 | + detect out-of-bounds access to heap, stack, and globals, use after free, use |
| 11 | + after return, double free, invalid free, memory leaks. |
| 12 | +* [LeakSanitizer][clang-lsan] a run-time memory leak detector. |
| 13 | +* [MemorySanitizer][clang-msan] a detector of uninitialized reads. |
| 14 | +* [ThreadSanitizer][clang-tsan] a fast data race detector. |
| 15 | + |
| 16 | +To enable a sanitizer compile with `-Zsanitizer=...` option, where value is one |
| 17 | +of `address`, `leak`, `memory` or `thread`. |
| 18 | + |
| 19 | +# Examples |
| 20 | + |
| 21 | +This sections show various issues that can be detected with sanitizers. For |
| 22 | +simplicity, the examples are prepared under assumption that optimization level |
| 23 | +used is zero. |
| 24 | + |
| 25 | +## AddressSanitizer |
| 26 | + |
| 27 | +Stack buffer overflow: |
| 28 | + |
| 29 | +```shell |
| 30 | +$ cat a.rs |
| 31 | +fn main() { |
| 32 | + let xs = [0, 1, 2, 3]; |
| 33 | + let _y = unsafe { *xs.as_ptr().offset(4) }; |
| 34 | +} |
| 35 | +$ rustc -Zsanitizer=address a.rs |
| 36 | +$ ./a |
| 37 | +================================================================= |
| 38 | +==10029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcc15f43d0 at pc 0x55f77dc015c5 bp 0x7ffcc15f4390 sp 0x7ffcc15f4388 |
| 39 | +READ of size 4 at 0x7ffcc15f43d0 thread T0 |
| 40 | + #0 0x55f77dc015c4 in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa5c4) |
| 41 | + #1 0x55f77dc01cdb in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::haa8c76d1faa7b7ca (/tmp/a+0xacdb) |
| 42 | + #2 0x55f77dc90f02 in std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::hfeb9a1aef9ac820d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:48:12 |
| 43 | + #3 0x55f77dc90f02 in std::panicking::try::do_call::h12f0919717b8e0a6 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:288:39 |
| 44 | + #4 0x55f77dc926c9 in __rust_maybe_catch_panic /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libpanic_unwind/lib.rs:80:7 |
| 45 | + #5 0x55f77dc9197c in std::panicking::try::h413b21cdcd6cfd86 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:267:12 |
| 46 | + #6 0x55f77dc9197c in std::panic::catch_unwind::hc5cc8ef2fd73424d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panic.rs:396:8 |
| 47 | + #7 0x55f77dc9197c in std::rt::lang_start_internal::h2039f418ab92218f /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:47:24 |
| 48 | + #8 0x55f77dc01c61 in std::rt::lang_start::ha905d28f6b61d691 (/tmp/a+0xac61) |
| 49 | + #9 0x55f77dc0163a in main (/tmp/a+0xa63a) |
| 50 | + #10 0x7f9b3cf5bbba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba) |
| 51 | + #11 0x55f77dc01289 in _start (/tmp/a+0xa289) |
| 52 | + |
| 53 | +Address 0x7ffcc15f43d0 is located in stack of thread T0 at offset 48 in frame |
| 54 | + #0 0x55f77dc0135f in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa35f) |
| 55 | + |
| 56 | + This frame has 1 object(s): |
| 57 | + [32, 48) 'xs' <== Memory access at offset 48 overflows this variable |
| 58 | +HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork |
| 59 | + (longjmp and C++ exceptions *are* supported) |
| 60 | +SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/a+0xa5c4) in a::main::hab3bd2a745c2d0ac |
| 61 | +Shadow bytes around the buggy address: |
| 62 | + 0x1000182b6820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 63 | + 0x1000182b6830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 64 | + 0x1000182b6840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 65 | + 0x1000182b6850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 66 | + 0x1000182b6860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 67 | +=>0x1000182b6870: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00 |
| 68 | + 0x1000182b6880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 69 | + 0x1000182b6890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 70 | + 0x1000182b68a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 71 | + 0x1000182b68b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 72 | + 0x1000182b68c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 73 | +Shadow byte legend (one shadow byte represents 8 application bytes): |
| 74 | + Addressable: 00 |
| 75 | + Partially addressable: 01 02 03 04 05 06 07 |
| 76 | + Heap left redzone: fa |
| 77 | + Freed heap region: fd |
| 78 | + Stack left redzone: f1 |
| 79 | + Stack mid redzone: f2 |
| 80 | + Stack right redzone: f3 |
| 81 | + Stack after return: f5 |
| 82 | + Stack use after scope: f8 |
| 83 | + Global redzone: f9 |
| 84 | + Global init order: f6 |
| 85 | + Poisoned by user: f7 |
| 86 | + Container overflow: fc |
| 87 | + Array cookie: ac |
| 88 | + Intra object redzone: bb |
| 89 | + ASan internal: fe |
| 90 | + Left alloca redzone: ca |
| 91 | + Right alloca redzone: cb |
| 92 | + Shadow gap: cc |
| 93 | +==10029==ABORTING |
| 94 | +``` |
| 95 | +
|
| 96 | +## MemorySanitizer |
| 97 | +
|
| 98 | +Use of uninitialized memory. Note that we are using `-Zbuild-std` to instrument |
| 99 | +standard library, and passing `-msan-track-origins=2` to the LLVM to track |
| 100 | +origins of uninitialized memory: |
| 101 | +
|
| 102 | +```shell |
| 103 | +$ cat src/main.rs |
| 104 | +use std::mem::MaybeUninit; |
| 105 | + |
| 106 | +fn main() { |
| 107 | + unsafe { |
| 108 | + let a = MaybeUninit::<[usize; 4]>::uninit(); |
| 109 | + let a = a.assume_init(); |
| 110 | + println!("{}", a[2]); |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +$ env RUSTFLAGS="-Zsanitizer=memory -Cllvm-args=-msan-track-origins=2" cargo -Zbuild-std run --target x86_64-unknown-linux-gnu |
| 115 | +==9416==WARNING: MemorySanitizer: use-of-uninitialized-value |
| 116 | + #0 0x560c04f7488a in core::fmt::num::imp::fmt_u64::haa293b0b098501ca $RUST/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/src/libcore/fmt/num.rs:202:16 |
| 117 | +... |
| 118 | + Uninitialized value was stored to memory at |
| 119 | + #0 0x560c04ae898a in __msan_memcpy.part.0 $RUST/src/llvm-project/compiler-rt/lib/msan/msan_interceptors.cc:1558:3 |
| 120 | + #1 0x560c04b2bf88 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:6:16 |
| 121 | + |
| 122 | + Uninitialized value was created by an allocation of 'a' in the stack frame of function '_ZN6memory4main17hd2333c1899d997f5E' |
| 123 | + #0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3 |
| 124 | +``` |
| 125 | +
|
| 126 | +
|
| 127 | +# Instrumentation of external dependencies and std |
| 128 | +
|
| 129 | +The sanitizers to varying degrees work correctly with partially instrumented |
| 130 | +code. On the one extreme is LeakSanitizer that doesn't use any compile time |
| 131 | +instrumentation, on the other is MemorySanitizer that requires that all program |
| 132 | +code to be instrumented (failing to achieve that will inevitably result in |
| 133 | +false positives). |
| 134 | +
|
| 135 | +It is strongly recommended to combine sanitizers with recompiled and |
| 136 | +instrumented standard library, for example using [cargo `-Zbuild-std` |
| 137 | +functionality][build-std]. |
| 138 | +
|
| 139 | +[build-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std |
| 140 | +
|
| 141 | +# Build scripts and procedural macros |
| 142 | +
|
| 143 | +Use of sanitizers together with build scripts and procedural macros is |
| 144 | +technically possible, but in almost all cases it would be best avoided. This |
| 145 | +is especially true for procedural macros which would require an instrumented |
| 146 | +version of rustc. |
| 147 | +
|
| 148 | +In more practical terms when using cargo always remember to pass `--target` |
| 149 | +flag, so that rustflags will not be applied to build scripts and procedural |
| 150 | +macros. |
| 151 | +
|
| 152 | +# Additional Information |
| 153 | +
|
| 154 | +* [Sanitizers project page](https://github.com/google/sanitizers/wiki/) |
| 155 | +* [AddressSanitizer in Clang][clang-asan] |
| 156 | +* [LeakSanitizer in Clang][clang-lsan] |
| 157 | +* [MemorySanitizer in Clang][clang-msan] |
| 158 | +* [ThreadSanitizer in Clang][clang-tsan] |
| 159 | +
|
| 160 | +[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html |
| 161 | +[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html |
| 162 | +[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html |
| 163 | +[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html |
0 commit comments