|
1 | 1 | # `sanitizer`
|
2 | 2 |
|
3 |
| -The tracking issue for this feature is: [#39699](https://github.com/rust-lang/rust/issues/39699). |
| 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). |
4 | 7 |
|
5 | 8 | ------------------------
|
6 | 9 |
|
7 | 10 | This feature allows for use of one of following sanitizers:
|
8 | 11 |
|
9 | 12 | * [AddressSanitizer][clang-asan] a fast memory error detector.
|
| 13 | +* [ControlFlowIntegrity][clang-cfi] LLVM Control Flow Integrity (CFI) provides |
| 14 | + forward-edge control flow protection. |
10 | 15 | * [HWAddressSanitizer][clang-hwasan] a memory error detector similar to
|
11 | 16 | AddressSanitizer, but based on partial hardware assistance.
|
12 | 17 | * [LeakSanitizer][clang-lsan] a run-time memory leak detector.
|
13 | 18 | * [MemorySanitizer][clang-msan] a detector of uninitialized reads.
|
14 | 19 | * [ThreadSanitizer][clang-tsan] a fast data race detector.
|
15 | 20 |
|
16 |
| -To enable a sanitizer compile with `-Zsanitizer=address`, |
| 21 | +To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`, |
17 | 22 | `-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or
|
18 | 23 | `-Zsanitizer=thread`.
|
19 | 24 |
|
@@ -177,6 +182,176 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
|
177 | 182 | ==39249==ABORTING
|
178 | 183 | ```
|
179 | 184 |
|
| 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 |
| 202 | +#![feature(asm, naked_functions)] |
| 203 | +
|
| 204 | +use std::mem; |
| 205 | +
|
| 206 | +fn add_one(x: i32) -> i32 { |
| 207 | + x + 1 |
| 208 | +} |
| 209 | +
|
| 210 | +#[naked] |
| 211 | +pub extern "C" fn add_two(x: i32) { |
| 212 | + // x + 2 preceeded by a landing pad/nop block |
| 213 | + unsafe { |
| 214 | + asm!( |
| 215 | + " |
| 216 | + nop |
| 217 | + nop |
| 218 | + nop |
| 219 | + nop |
| 220 | + nop |
| 221 | + nop |
| 222 | + nop |
| 223 | + nop |
| 224 | + nop |
| 225 | + lea rax, [rdi+2] |
| 226 | + ret |
| 227 | + ", |
| 228 | + options(noreturn) |
| 229 | + ); |
| 230 | + } |
| 231 | +} |
| 232 | +
|
| 233 | +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { |
| 234 | + f(arg) + f(arg) |
| 235 | +} |
| 236 | +
|
| 237 | +fn main() { |
| 238 | + let answer = do_twice(add_one, 5); |
| 239 | +
|
| 240 | + println!("The answer is: {}", answer); |
| 241 | +
|
| 242 | + println!("With CFI enabled, you should not see the next answer"); |
| 243 | + let f: fn(i32) -> i32 = unsafe { |
| 244 | + // Offsets 0-8 make it land in the landing pad/nop block, and offsets 1-8 are |
| 245 | + // invalid branch/call destinations (i.e., within the body of the function). |
| 246 | + mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) |
| 247 | + }; |
| 248 | + let next_answer = do_twice(f, 5); |
| 249 | +
|
| 250 | + println!("The next answer is: {}", next_answer); |
| 251 | +} |
| 252 | +``` |
| 253 | +Fig. 1. Modified example from the [Advanced Functions and |
| 254 | +Closures][rust-book-ch19-05] chapter of the [The Rust Programming |
| 255 | +Language][rust-book] book. |
| 256 | +
|
| 257 | +[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) |
| 258 | +
|
| 259 | +```shell |
| 260 | +$ rustc rust_cfi.rs -o rust_cfi |
| 261 | +$ ./rust_cfi |
| 262 | +The answer is: 12 |
| 263 | +With CFI enabled, you should not see the next answer |
| 264 | +The next answer is: 14 |
| 265 | +$ |
| 266 | +``` |
| 267 | +Fig. 2. Build and execution of the modified example with LLVM CFI disabled. |
| 268 | +
|
| 269 | +[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) |
| 270 | +
|
| 271 | +```shell |
| 272 | +$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi |
| 273 | +$ ./rust_cfi |
| 274 | +The answer is: 12 |
| 275 | +With CFI enabled, you should not see the next answer |
| 276 | +Illegal instruction |
| 277 | +$ |
| 278 | +``` |
| 279 | +Fig. 3. Build and execution of the modified example with LLVM CFI enabled. |
| 280 | +
|
| 281 | +When LLVM CFI is enabled, if there are any attempts to change/hijack control |
| 282 | +flow using an indirect branch/call to an invalid destination, the execution is |
| 283 | +terminated (see Fig. 3). |
| 284 | +
|
| 285 | +```rust |
| 286 | +use std::mem; |
| 287 | +
|
| 288 | +fn add_one(x: i32) -> i32 { |
| 289 | + x + 1 |
| 290 | +} |
| 291 | +
|
| 292 | +fn add_two(x: i32, _y: i32) -> i32 { |
| 293 | + x + 2 |
| 294 | +} |
| 295 | +
|
| 296 | +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { |
| 297 | + f(arg) + f(arg) |
| 298 | +} |
| 299 | +
|
| 300 | +fn main() { |
| 301 | + let answer = do_twice(add_one, 5); |
| 302 | +
|
| 303 | + println!("The answer is: {}", answer); |
| 304 | +
|
| 305 | + println!("With CFI enabled, you should not see the next answer"); |
| 306 | + let f: fn(i32) -> i32 = |
| 307 | + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; |
| 308 | + let next_answer = do_twice(f, 5); |
| 309 | +
|
| 310 | + println!("The next answer is: {}", next_answer); |
| 311 | +} |
| 312 | +``` |
| 313 | +Fig. 4. Another modified example from the [Advanced Functions and |
| 314 | +Closures][rust-book-ch19-05] chapter of the [The Rust Programming |
| 315 | +Language][rust-book] book. |
| 316 | +
|
| 317 | +[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) |
| 318 | +
|
| 319 | +```shell |
| 320 | +$ rustc rust_cfi.rs -o rust_cfi |
| 321 | +$ ./rust_cfi |
| 322 | +The answer is: 12 |
| 323 | +With CFI enabled, you should not see the next answer |
| 324 | +The next answer is: 14 |
| 325 | +$ |
| 326 | +``` |
| 327 | +Fig. 5. Build and execution of the modified example with LLVM CFI disabled. |
| 328 | +
|
| 329 | +[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) |
| 330 | +
|
| 331 | +```shell |
| 332 | +$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi |
| 333 | +$ ./rust_cfi |
| 334 | +The answer is: 12 |
| 335 | +With CFI enabled, you should not see the next answer |
| 336 | +Illegal instruction |
| 337 | +$ |
| 338 | +``` |
| 339 | +Fig. 6. Build and execution of the modified example with LLVM CFI enabled. |
| 340 | +
|
| 341 | +When LLVM CFI is enabled, if there are any attempts to change/hijack control |
| 342 | +flow using an indirect branch/call to a function with different number of |
| 343 | +arguments than intended/passed in the call/branch site, the execution is also |
| 344 | +terminated (see Fig. 6). |
| 345 | +
|
| 346 | +Forward-edge control flow protection not only by aggregating function pointers |
| 347 | +in groups identified by their number of arguments, but also their argument |
| 348 | +types, will also be provided in later work by defining and using compatible type |
| 349 | +identifiers (see Type metadata in the design document in the tracking |
| 350 | +issue [#89653](https://github.com/rust-lang/rust/issues/89653)). |
| 351 | +
|
| 352 | +[rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html |
| 353 | +[rust-book]: https://doc.rust-lang.org/book/title-page.html |
| 354 | +
|
180 | 355 | # HWAddressSanitizer
|
181 | 356 |
|
182 | 357 | HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much
|
@@ -404,12 +579,14 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
|
404 | 579 |
|
405 | 580 | * [Sanitizers project page](https://github.com/google/sanitizers/wiki/)
|
406 | 581 | * [AddressSanitizer in Clang][clang-asan]
|
| 582 | +* [ControlFlowIntegrity in Clang][clang-cfi] |
407 | 583 | * [HWAddressSanitizer in Clang][clang-hwasan]
|
408 | 584 | * [LeakSanitizer in Clang][clang-lsan]
|
409 | 585 | * [MemorySanitizer in Clang][clang-msan]
|
410 | 586 | * [ThreadSanitizer in Clang][clang-tsan]
|
411 | 587 |
|
412 | 588 | [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
|
| 589 | +[clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html |
413 | 590 | [clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
|
414 | 591 | [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
|
415 | 592 | [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
|
|
0 commit comments