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