]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | # `sanitizer` |
2 | ||
3c0e092e XL |
3 | The tracking issues for this feature are: |
4 | ||
5 | * [#39699](https://github.com/rust-lang/rust/issues/39699). | |
6 | * [#89653](https://github.com/rust-lang/rust/issues/89653). | |
dfeec247 XL |
7 | |
8 | ------------------------ | |
9 | ||
10 | This feature allows for use of one of following sanitizers: | |
11 | ||
ba9703b0 | 12 | * [AddressSanitizer][clang-asan] a fast memory error detector. |
3c0e092e XL |
13 | * [ControlFlowIntegrity][clang-cfi] LLVM Control Flow Integrity (CFI) provides |
14 | forward-edge control flow protection. | |
6a06907d XL |
15 | * [HWAddressSanitizer][clang-hwasan] a memory error detector similar to |
16 | AddressSanitizer, but based on partial hardware assistance. | |
dfeec247 XL |
17 | * [LeakSanitizer][clang-lsan] a run-time memory leak detector. |
18 | * [MemorySanitizer][clang-msan] a detector of uninitialized reads. | |
19 | * [ThreadSanitizer][clang-tsan] a fast data race detector. | |
20 | ||
3c0e092e | 21 | To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`, |
6a06907d XL |
22 | `-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or |
23 | `-Zsanitizer=thread`. | |
dfeec247 | 24 | |
ba9703b0 | 25 | # AddressSanitizer |
dfeec247 | 26 | |
ba9703b0 XL |
27 | AddressSanitizer is a memory error detector. It can detect the following types |
28 | of bugs: | |
dfeec247 | 29 | |
ba9703b0 XL |
30 | * Out of bound accesses to heap, stack and globals |
31 | * Use after free | |
32 | * Use after return (runtime flag `ASAN_OPTIONS=detect_stack_use_after_return=1`) | |
33 | * Use after scope | |
34 | * Double-free, invalid free | |
35 | * Memory leaks | |
36 | ||
3dfed10e XL |
37 | The memory leak detection is enabled by default on Linux, and can be enabled |
38 | with runtime flag `ASAN_OPTIONS=detect_leaks=1` on macOS. | |
39 | ||
ba9703b0 XL |
40 | AddressSanitizer is supported on the following targets: |
41 | ||
5869c6ff XL |
42 | * `aarch64-apple-darwin` |
43 | * `aarch64-fuchsia` | |
44 | * `aarch64-unknown-linux-gnu` | |
ba9703b0 | 45 | * `x86_64-apple-darwin` |
5869c6ff XL |
46 | * `x86_64-fuchsia` |
47 | * `x86_64-unknown-freebsd` | |
ba9703b0 XL |
48 | * `x86_64-unknown-linux-gnu` |
49 | ||
50 | AddressSanitizer works with non-instrumented code although it will impede its | |
51 | ability to detect some bugs. It is not expected to produce false positive | |
52 | reports. | |
53 | ||
54 | ## Examples | |
dfeec247 XL |
55 | |
56 | Stack buffer overflow: | |
57 | ||
ba9703b0 | 58 | ```rust |
dfeec247 XL |
59 | fn main() { |
60 | let xs = [0, 1, 2, 3]; | |
61 | let _y = unsafe { *xs.as_ptr().offset(4) }; | |
62 | } | |
ba9703b0 XL |
63 | ``` |
64 | ||
65 | ```shell | |
66 | $ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address | |
67 | $ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu | |
68 | ==37882==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe400e6250 at pc 0x5609a841fb20 bp 0x7ffe400e6210 sp 0x7ffe400e6208 | |
69 | READ of size 4 at 0x7ffe400e6250 thread T0 | |
70 | #0 0x5609a841fb1f in example::main::h628ffc6626ed85b2 /.../src/main.rs:3:23 | |
71 | ... | |
72 | ||
73 | Address 0x7ffe400e6250 is located in stack of thread T0 at offset 48 in frame | |
74 | #0 0x5609a841f8af in example::main::h628ffc6626ed85b2 /.../src/main.rs:1 | |
dfeec247 XL |
75 | |
76 | This frame has 1 object(s): | |
ba9703b0 | 77 | [32, 48) 'xs' (line 2) <== Memory access at offset 48 overflows this variable |
dfeec247 XL |
78 | HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork |
79 | (longjmp and C++ exceptions *are* supported) | |
ba9703b0 | 80 | SUMMARY: AddressSanitizer: stack-buffer-overflow /.../src/main.rs:3:23 in example::main::h628ffc6626ed85b2 |
dfeec247 | 81 | Shadow bytes around the buggy address: |
ba9703b0 XL |
82 | 0x100048014bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
83 | 0x100048014c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
84 | 0x100048014c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
85 | 0x100048014c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
86 | 0x100048014c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
87 | =>0x100048014c40: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00 | |
88 | 0x100048014c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
89 | 0x100048014c60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
90 | 0x100048014c70: f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 00 00 00 00 | |
91 | 0x100048014c80: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 | |
92 | 0x100048014c90: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 | |
dfeec247 XL |
93 | Shadow byte legend (one shadow byte represents 8 application bytes): |
94 | Addressable: 00 | |
ba9703b0 | 95 | Partially addressable: 01 02 03 04 05 06 07 |
dfeec247 XL |
96 | Heap left redzone: fa |
97 | Freed heap region: fd | |
98 | Stack left redzone: f1 | |
99 | Stack mid redzone: f2 | |
100 | Stack right redzone: f3 | |
101 | Stack after return: f5 | |
102 | Stack use after scope: f8 | |
103 | Global redzone: f9 | |
104 | Global init order: f6 | |
105 | Poisoned by user: f7 | |
106 | Container overflow: fc | |
107 | Array cookie: ac | |
108 | Intra object redzone: bb | |
109 | ASan internal: fe | |
110 | Left alloca redzone: ca | |
111 | Right alloca redzone: cb | |
112 | Shadow gap: cc | |
ba9703b0 | 113 | ==37882==ABORTING |
dfeec247 XL |
114 | ``` |
115 | ||
74b04a01 XL |
116 | Use of a stack object after its scope has already ended: |
117 | ||
ba9703b0 | 118 | ```rust |
74b04a01 XL |
119 | static mut P: *mut usize = std::ptr::null_mut(); |
120 | ||
121 | fn main() { | |
122 | unsafe { | |
123 | { | |
124 | let mut x = 0; | |
125 | P = &mut x; | |
126 | } | |
127 | std::ptr::write_volatile(P, 123); | |
128 | } | |
129 | } | |
ba9703b0 XL |
130 | ``` |
131 | ||
132 | ```shell | |
133 | $ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address | |
134 | $ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu | |
74b04a01 | 135 | ================================================================= |
ba9703b0 XL |
136 | ==39249==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc7ed3e1a0 at pc 0x55c98b262a8e bp 0x7ffc7ed3e050 sp 0x7ffc7ed3e048 |
137 | WRITE of size 8 at 0x7ffc7ed3e1a0 thread T0 | |
138 | #0 0x55c98b262a8d in core::ptr::write_volatile::he21f1df5a82f329a /.../src/rust/src/libcore/ptr/mod.rs:1048:5 | |
139 | #1 0x55c98b262cd2 in example::main::h628ffc6626ed85b2 /.../src/main.rs:9:9 | |
140 | ... | |
141 | ||
142 | Address 0x7ffc7ed3e1a0 is located in stack of thread T0 at offset 32 in frame | |
143 | #0 0x55c98b262bdf in example::main::h628ffc6626ed85b2 /.../src/main.rs:3 | |
74b04a01 XL |
144 | |
145 | This frame has 1 object(s): | |
ba9703b0 | 146 | [32, 40) 'x' (line 6) <== Memory access at offset 32 is inside this variable |
74b04a01 XL |
147 | HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork |
148 | (longjmp and C++ exceptions *are* supported) | |
ba9703b0 | 149 | SUMMARY: AddressSanitizer: stack-use-after-scope /.../src/rust/src/libcore/ptr/mod.rs:1048:5 in core::ptr::write_volatile::he21f1df5a82f329a |
74b04a01 | 150 | Shadow bytes around the buggy address: |
ba9703b0 XL |
151 | 0x10000fd9fbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
152 | 0x10000fd9fbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
153 | 0x10000fd9fc00: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 | |
154 | 0x10000fd9fc10: f8 f8 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 | |
155 | 0x10000fd9fc20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
156 | =>0x10000fd9fc30: f1 f1 f1 f1[f8]f3 f3 f3 00 00 00 00 00 00 00 00 | |
157 | 0x10000fd9fc40: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 | |
158 | 0x10000fd9fc50: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 | |
159 | 0x10000fd9fc60: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 | |
160 | 0x10000fd9fc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
161 | 0x10000fd9fc80: 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 | |
74b04a01 XL |
162 | Shadow byte legend (one shadow byte represents 8 application bytes): |
163 | Addressable: 00 | |
164 | Partially addressable: 01 02 03 04 05 06 07 | |
165 | Heap left redzone: fa | |
166 | Freed heap region: fd | |
167 | Stack left redzone: f1 | |
168 | Stack mid redzone: f2 | |
169 | Stack right redzone: f3 | |
170 | Stack after return: f5 | |
171 | Stack use after scope: f8 | |
172 | Global redzone: f9 | |
173 | Global init order: f6 | |
174 | Poisoned by user: f7 | |
175 | Container overflow: fc | |
176 | Array cookie: ac | |
177 | Intra object redzone: bb | |
178 | ASan internal: fe | |
179 | Left alloca redzone: ca | |
180 | Right alloca redzone: cb | |
181 | Shadow gap: cc | |
ba9703b0 | 182 | ==39249==ABORTING |
74b04a01 XL |
183 | ``` |
184 | ||
3c0e092e XL |
185 | # ControlFlowIntegrity |
186 | ||
187 | The LLVM Control Flow Integrity (CFI) support in the Rust compiler initially | |
188 | provides forward-edge control flow protection for Rust-compiled code only by | |
189 | aggregating function pointers in groups identified by their number of arguments. | |
190 | ||
191 | Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed | |
192 | binaries" (i.e., for when C or C++ and Rust -compiled code share the same | |
193 | virtual address space) will be provided in later work by defining and using | |
194 | compatible type identifiers (see Type metadata in the design document in the | |
195 | tracking issue [#89653](https://github.com/rust-lang/rust/issues/89653)). | |
196 | ||
197 | LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto). | |
198 | ||
199 | ## Example | |
200 | ||
201 | ```text | |
a2a8927a | 202 | #![feature(naked_functions)] |
3c0e092e | 203 | |
a2a8927a | 204 | use std::arch::asm; |
3c0e092e XL |
205 | use std::mem; |
206 | ||
207 | fn add_one(x: i32) -> i32 { | |
208 | x + 1 | |
209 | } | |
210 | ||
211 | #[naked] | |
212 | pub extern "C" fn add_two(x: i32) { | |
213 | // x + 2 preceeded by a landing pad/nop block | |
214 | unsafe { | |
215 | asm!( | |
216 | " | |
217 | nop | |
218 | nop | |
219 | nop | |
220 | nop | |
221 | nop | |
222 | nop | |
223 | nop | |
224 | nop | |
225 | nop | |
226 | lea rax, [rdi+2] | |
227 | ret | |
228 | ", | |
229 | options(noreturn) | |
230 | ); | |
231 | } | |
232 | } | |
233 | ||
234 | fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { | |
235 | f(arg) + f(arg) | |
236 | } | |
237 | ||
238 | fn main() { | |
239 | let answer = do_twice(add_one, 5); | |
240 | ||
241 | println!("The answer is: {}", answer); | |
242 | ||
243 | println!("With CFI enabled, you should not see the next answer"); | |
244 | let f: fn(i32) -> i32 = unsafe { | |
245 | // Offsets 0-8 make it land in the landing pad/nop block, and offsets 1-8 are | |
246 | // invalid branch/call destinations (i.e., within the body of the function). | |
247 | mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) | |
248 | }; | |
249 | let next_answer = do_twice(f, 5); | |
250 | ||
251 | println!("The next answer is: {}", next_answer); | |
252 | } | |
253 | ``` | |
254 | Fig. 1. Modified example from the [Advanced Functions and | |
255 | Closures][rust-book-ch19-05] chapter of the [The Rust Programming | |
256 | Language][rust-book] book. | |
257 | ||
258 | [//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) | |
259 | ||
260 | ```shell | |
261 | $ rustc rust_cfi.rs -o rust_cfi | |
262 | $ ./rust_cfi | |
263 | The answer is: 12 | |
264 | With CFI enabled, you should not see the next answer | |
265 | The next answer is: 14 | |
266 | $ | |
267 | ``` | |
268 | Fig. 2. Build and execution of the modified example with LLVM CFI disabled. | |
269 | ||
270 | [//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) | |
271 | ||
272 | ```shell | |
273 | $ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi | |
274 | $ ./rust_cfi | |
275 | The answer is: 12 | |
276 | With CFI enabled, you should not see the next answer | |
277 | Illegal instruction | |
278 | $ | |
279 | ``` | |
280 | Fig. 3. Build and execution of the modified example with LLVM CFI enabled. | |
281 | ||
282 | When LLVM CFI is enabled, if there are any attempts to change/hijack control | |
283 | flow using an indirect branch/call to an invalid destination, the execution is | |
284 | terminated (see Fig. 3). | |
285 | ||
286 | ```rust | |
287 | use std::mem; | |
288 | ||
289 | fn add_one(x: i32) -> i32 { | |
290 | x + 1 | |
291 | } | |
292 | ||
293 | fn add_two(x: i32, _y: i32) -> i32 { | |
294 | x + 2 | |
295 | } | |
296 | ||
297 | fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { | |
298 | f(arg) + f(arg) | |
299 | } | |
300 | ||
301 | fn main() { | |
302 | let answer = do_twice(add_one, 5); | |
303 | ||
304 | println!("The answer is: {}", answer); | |
305 | ||
306 | println!("With CFI enabled, you should not see the next answer"); | |
307 | let f: fn(i32) -> i32 = | |
308 | unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; | |
309 | let next_answer = do_twice(f, 5); | |
310 | ||
311 | println!("The next answer is: {}", next_answer); | |
312 | } | |
313 | ``` | |
314 | Fig. 4. Another modified example from the [Advanced Functions and | |
315 | Closures][rust-book-ch19-05] chapter of the [The Rust Programming | |
316 | Language][rust-book] book. | |
317 | ||
318 | [//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) | |
319 | ||
320 | ```shell | |
321 | $ rustc rust_cfi.rs -o rust_cfi | |
322 | $ ./rust_cfi | |
323 | The answer is: 12 | |
324 | With CFI enabled, you should not see the next answer | |
325 | The next answer is: 14 | |
326 | $ | |
327 | ``` | |
328 | Fig. 5. Build and execution of the modified example with LLVM CFI disabled. | |
329 | ||
330 | [//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) | |
331 | ||
332 | ```shell | |
333 | $ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi | |
334 | $ ./rust_cfi | |
335 | The answer is: 12 | |
336 | With CFI enabled, you should not see the next answer | |
337 | Illegal instruction | |
338 | $ | |
339 | ``` | |
340 | Fig. 6. Build and execution of the modified example with LLVM CFI enabled. | |
341 | ||
342 | When LLVM CFI is enabled, if there are any attempts to change/hijack control | |
343 | flow using an indirect branch/call to a function with different number of | |
344 | arguments than intended/passed in the call/branch site, the execution is also | |
345 | terminated (see Fig. 6). | |
346 | ||
347 | Forward-edge control flow protection not only by aggregating function pointers | |
348 | in groups identified by their number of arguments, but also their argument | |
349 | types, will also be provided in later work by defining and using compatible type | |
350 | identifiers (see Type metadata in the design document in the tracking | |
351 | issue [#89653](https://github.com/rust-lang/rust/issues/89653)). | |
352 | ||
353 | [rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html | |
354 | [rust-book]: https://doc.rust-lang.org/book/title-page.html | |
355 | ||
6a06907d XL |
356 | # HWAddressSanitizer |
357 | ||
358 | HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much | |
359 | less memory. | |
360 | ||
361 | HWAddressSanitizer is supported on the following targets: | |
362 | ||
363 | * `aarch64-linux-android` | |
364 | * `aarch64-unknown-linux-gnu` | |
365 | ||
366 | HWAddressSanitizer requires `tagged-globals` target feature to instrument | |
367 | globals. To enable this target feature compile with `-C | |
368 | target-feature=+tagged-globals` | |
369 | ||
370 | ## Example | |
371 | ||
372 | Heap buffer overflow: | |
373 | ||
374 | ```rust | |
375 | fn main() { | |
376 | let xs = vec![0, 1, 2, 3]; | |
377 | let _y = unsafe { *xs.as_ptr().offset(4) }; | |
378 | } | |
379 | ``` | |
380 | ||
381 | ```shell | |
382 | $ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C | |
383 | linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target | |
384 | aarch64-unknown-linux-gnu | |
385 | ``` | |
386 | ||
387 | ```shell | |
388 | $ ./main | |
389 | ==241==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffff0050 at pc 0xaaaae0ae4a98 | |
390 | READ of size 4 at 0xefdeffff0050 tags: 2c/00 (ptr/mem) in thread T0 | |
391 | #0 0xaaaae0ae4a94 (/.../main+0x54a94) | |
392 | ... | |
393 | ||
394 | [0xefdeffff0040,0xefdeffff0060) is a small allocated heap chunk; size: 32 offset: 16 | |
395 | 0xefdeffff0050 is located 0 bytes to the right of 16-byte region [0xefdeffff0040,0xefdeffff0050) | |
396 | allocated here: | |
397 | #0 0xaaaae0acb80c (/.../main+0x3b80c) | |
398 | ... | |
399 | ||
400 | Thread: T0 0xeffe00002000 stack: [0xffffc28ad000,0xffffc30ad000) sz: 8388608 tls: [0xffffaa10a020,0xffffaa10a7d0) | |
401 | Memory tags around the buggy address (one tag corresponds to 16 bytes): | |
402 | 0xfefcefffef80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
403 | 0xfefcefffef90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
404 | 0xfefcefffefa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
405 | 0xfefcefffefb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
406 | 0xfefcefffefc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
407 | 0xfefcefffefd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
408 | 0xfefcefffefe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
409 | 0xfefcefffeff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
410 | =>0xfefceffff000: d7 d7 05 00 2c [00] 00 00 00 00 00 00 00 00 00 00 | |
411 | 0xfefceffff010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
412 | 0xfefceffff020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
413 | 0xfefceffff030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
414 | 0xfefceffff040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
415 | 0xfefceffff050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
416 | 0xfefceffff060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
417 | 0xfefceffff070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
418 | 0xfefceffff080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
419 | Tags for short granules around the buggy address (one tag corresponds to 16 bytes): | |
420 | 0xfefcefffeff0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. | |
421 | =>0xfefceffff000: .. .. 8c .. .. [..] .. .. .. .. .. .. .. .. .. .. | |
422 | 0xfefceffff010: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. | |
423 | See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags | |
424 | Registers where the failure occurred (pc 0xaaaae0ae4a98): | |
425 | x0 2c00efdeffff0050 x1 0000000000000004 x2 0000000000000004 x3 0000000000000000 | |
426 | x4 0000fffefc30ac37 x5 000000000000005d x6 00000ffffc30ac37 x7 0000efff00000000 | |
427 | x8 2c00efdeffff0050 x9 0200efff00000000 x10 0000000000000000 x11 0200efff00000000 | |
428 | x12 0200effe00000310 x13 0200effe00000310 x14 0000000000000008 x15 5d00ffffc30ac360 | |
429 | x16 0000aaaae0ad062c x17 0000000000000003 x18 0000000000000001 x19 0000ffffc30ac658 | |
430 | x20 4e00ffffc30ac6e0 x21 0000aaaae0ac5e10 x22 0000000000000000 x23 0000000000000000 | |
431 | x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000 | |
432 | x28 0000000000000000 x29 0000ffffc30ac5a0 x30 0000aaaae0ae4a98 | |
433 | SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94) | |
434 | ``` | |
435 | ||
5869c6ff XL |
436 | # LeakSanitizer |
437 | ||
438 | LeakSanitizer is run-time memory leak detector. | |
439 | ||
440 | LeakSanitizer is supported on the following targets: | |
441 | ||
442 | * `aarch64-apple-darwin` | |
443 | * `aarch64-unknown-linux-gnu` | |
444 | * `x86_64-apple-darwin` | |
445 | * `x86_64-unknown-linux-gnu` | |
446 | ||
ba9703b0 XL |
447 | # MemorySanitizer |
448 | ||
5869c6ff XL |
449 | MemorySanitizer is detector of uninitialized reads. |
450 | ||
451 | MemorySanitizer is supported on the following targets: | |
452 | ||
453 | * `aarch64-unknown-linux-gnu` | |
454 | * `x86_64-unknown-freebsd` | |
455 | * `x86_64-unknown-linux-gnu` | |
ba9703b0 XL |
456 | |
457 | MemorySanitizer requires all program code to be instrumented. C/C++ dependencies | |
458 | need to be recompiled using Clang with `-fsanitize=memory` option. Failing to | |
459 | achieve that will result in false positive reports. | |
460 | ||
461 | ## Example | |
dfeec247 | 462 | |
ba9703b0 XL |
463 | Detecting the use of uninitialized memory. The `-Zbuild-std` flag rebuilds and |
464 | instruments the standard library, and is strictly necessary for the correct | |
6a06907d XL |
465 | operation of the tool. The `-Zsanitizer-memory-track-origins` enables tracking |
466 | of the origins of uninitialized memory: | |
dfeec247 | 467 | |
ba9703b0 | 468 | ```rust |
dfeec247 XL |
469 | use std::mem::MaybeUninit; |
470 | ||
471 | fn main() { | |
472 | unsafe { | |
473 | let a = MaybeUninit::<[usize; 4]>::uninit(); | |
474 | let a = a.assume_init(); | |
475 | println!("{}", a[2]); | |
476 | } | |
477 | } | |
ba9703b0 | 478 | ``` |
dfeec247 | 479 | |
ba9703b0 | 480 | ```shell |
74b04a01 | 481 | $ export \ |
74b04a01 XL |
482 | RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' \ |
483 | RUSTDOCFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' | |
484 | $ cargo clean | |
ba9703b0 | 485 | $ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu |
dfeec247 XL |
486 | ==9416==WARNING: MemorySanitizer: use-of-uninitialized-value |
487 | #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 | |
488 | ... | |
489 | Uninitialized value was stored to memory at | |
490 | #0 0x560c04ae898a in __msan_memcpy.part.0 $RUST/src/llvm-project/compiler-rt/lib/msan/msan_interceptors.cc:1558:3 | |
491 | #1 0x560c04b2bf88 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:6:16 | |
492 | ||
493 | Uninitialized value was created by an allocation of 'a' in the stack frame of function '_ZN6memory4main17hd2333c1899d997f5E' | |
494 | #0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3 | |
495 | ``` | |
496 | ||
ba9703b0 XL |
497 | # ThreadSanitizer |
498 | ||
499 | ThreadSanitizer is a data race detection tool. It is supported on the following | |
500 | targets: | |
501 | ||
5869c6ff XL |
502 | * `aarch64-apple-darwin` |
503 | * `aarch64-unknown-linux-gnu` | |
ba9703b0 | 504 | * `x86_64-apple-darwin` |
5869c6ff | 505 | * `x86_64-unknown-freebsd` |
ba9703b0 XL |
506 | * `x86_64-unknown-linux-gnu` |
507 | ||
508 | To work correctly ThreadSanitizer needs to be "aware" of all synchronization | |
509 | operations in a program. It generally achieves that through combination of | |
510 | library interception (for example synchronization performed through | |
511 | `pthread_mutex_lock` / `pthread_mutex_unlock`) and compile time instrumentation | |
512 | (e.g. atomic operations). Using it without instrumenting all the program code | |
513 | can lead to false positive reports. | |
514 | ||
515 | ThreadSanitizer does not support atomic fences `std::sync::atomic::fence`, | |
516 | nor synchronization performed using inline assembly code. | |
517 | ||
518 | ## Example | |
519 | ||
520 | ```rust | |
521 | static mut A: usize = 0; | |
522 | ||
523 | fn main() { | |
524 | let t = std::thread::spawn(|| { | |
525 | unsafe { A += 1 }; | |
526 | }); | |
527 | unsafe { A += 1 }; | |
528 | ||
529 | t.join().unwrap(); | |
530 | } | |
531 | ``` | |
532 | ||
533 | ```shell | |
534 | $ export RUSTFLAGS=-Zsanitizer=thread RUSTDOCFLAGS=-Zsanitizer=thread | |
535 | $ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu | |
536 | ================== | |
537 | WARNING: ThreadSanitizer: data race (pid=10574) | |
538 | Read of size 8 at 0x5632dfe3d030 by thread T1: | |
539 | #0 example::main::_$u7b$$u7b$closure$u7d$$u7d$::h23f64b0b2f8c9484 ../src/main.rs:5:18 (example+0x86cec) | |
540 | ... | |
541 | ||
542 | Previous write of size 8 at 0x5632dfe3d030 by main thread: | |
543 | #0 example::main::h628ffc6626ed85b2 /.../src/main.rs:7:14 (example+0x868c8) | |
544 | ... | |
545 | #11 main <null> (example+0x86a1a) | |
546 | ||
547 | Location is global 'example::A::h43ac149ddf992709' of size 8 at 0x5632dfe3d030 (example+0x000000bd9030) | |
548 | ``` | |
dfeec247 XL |
549 | |
550 | # Instrumentation of external dependencies and std | |
551 | ||
552 | The sanitizers to varying degrees work correctly with partially instrumented | |
553 | code. On the one extreme is LeakSanitizer that doesn't use any compile time | |
554 | instrumentation, on the other is MemorySanitizer that requires that all program | |
555 | code to be instrumented (failing to achieve that will inevitably result in | |
556 | false positives). | |
557 | ||
558 | It is strongly recommended to combine sanitizers with recompiled and | |
559 | instrumented standard library, for example using [cargo `-Zbuild-std` | |
560 | functionality][build-std]. | |
561 | ||
562 | [build-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std | |
563 | ||
564 | # Build scripts and procedural macros | |
565 | ||
566 | Use of sanitizers together with build scripts and procedural macros is | |
567 | technically possible, but in almost all cases it would be best avoided. This | |
568 | is especially true for procedural macros which would require an instrumented | |
569 | version of rustc. | |
570 | ||
571 | In more practical terms when using cargo always remember to pass `--target` | |
572 | flag, so that rustflags will not be applied to build scripts and procedural | |
573 | macros. | |
574 | ||
ba9703b0 XL |
575 | # Symbolizing the Reports |
576 | ||
577 | Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PATH`. | |
578 | ||
dfeec247 XL |
579 | # Additional Information |
580 | ||
581 | * [Sanitizers project page](https://github.com/google/sanitizers/wiki/) | |
582 | * [AddressSanitizer in Clang][clang-asan] | |
3c0e092e | 583 | * [ControlFlowIntegrity in Clang][clang-cfi] |
6a06907d | 584 | * [HWAddressSanitizer in Clang][clang-hwasan] |
dfeec247 XL |
585 | * [LeakSanitizer in Clang][clang-lsan] |
586 | * [MemorySanitizer in Clang][clang-msan] | |
587 | * [ThreadSanitizer in Clang][clang-tsan] | |
588 | ||
589 | [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html | |
3c0e092e | 590 | [clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html |
6a06907d | 591 | [clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html |
dfeec247 XL |
592 | [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html |
593 | [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html | |
594 | [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html |