From 08a1c566a792dcf9657d293155f7ada87746bb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 27 Jan 2020 00:00:00 +0000 Subject: [PATCH 1/3] Detect use-after-scope bugs with AddressSanitizer Enable use-after-scope checks by default when using AddressSanitizer. They allow to detect incorrect use of stack objects after their scope have already ended. The detection is based on LLVM lifetime intrinsics. To facilitate the use of this functionality, the lifetime intrinsics are now emitted regardless of optimization level if enabled sanitizer makes use of them. --- src/librustc_codegen_llvm/builder.rs | 15 +++++++++++---- src/rustllvm/PassWrapper.cpp | 3 ++- src/test/ui/sanitizer-use-after-scope.rs | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/sanitizer-use-after-scope.rs diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 357b0b6c451..c59b81eb4cc 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -7,7 +7,7 @@ use crate::value::Value; use libc::{c_char, c_uint}; use log::debug; -use rustc::session::config; +use rustc::session::config::{self, Sanitizer}; use rustc::ty::layout::{self, Align, Size, TyLayout}; use rustc::ty::{self, Ty, TyCtxt}; use rustc_codegen_ssa::base::to_immediate; @@ -1232,12 +1232,19 @@ pub fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value { } fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) { - if self.cx.sess().opts.optimize == config::OptLevel::No { + let size = size.bytes(); + if size == 0 { return; } - let size = size.bytes(); - if size == 0 { + let opts = &self.cx.sess().opts; + let emit = match opts.debugging_opts.sanitizer { + // Some sanitizer use lifetime intrinsics. When they are in use, + // emit lifetime intrinsics regardless of optimization level. + Some(Sanitizer::Address) | Some(Sanitizer::Memory) => true, + _ => opts.optimize != config::OptLevel::No, + }; + if !emit { return; } diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index eaa845a279f..fad70369807 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -87,8 +87,9 @@ extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) { const bool CompileKernel = false; + const bool UseAfterScope = true; - return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover)); + return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope)); } extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) { diff --git a/src/test/ui/sanitizer-use-after-scope.rs b/src/test/ui/sanitizer-use-after-scope.rs new file mode 100644 index 00000000000..6a2067e157a --- /dev/null +++ b/src/test/ui/sanitizer-use-after-scope.rs @@ -0,0 +1,18 @@ +// needs-sanitizer-support +// only-x86_64 +// +// compile-flags: -Zsanitizer=address +// run-fail +// error-pattern: ERROR: AddressSanitizer: stack-use-after-scope + +static mut P: *mut usize = std::ptr::null_mut(); + +fn main() { + unsafe { + { + let mut x = 0; + P = &mut x; + } + std::ptr::write_volatile(P, 123); + } +} From 461aada2fe0f1305b99b527b1b5ff8696a3e9ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 27 Jan 2020 00:00:00 +0000 Subject: [PATCH 2/3] Add use after scope example to the unstable book --- .../src/compiler-flags/sanitizer.md | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index cbb90bd3bb3..64bff2a6fe0 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -93,6 +93,80 @@ Shadow byte legend (one shadow byte represents 8 application bytes): ==10029==ABORTING ``` +Use of a stack object after its scope has already ended: + +```shell +$ cat b.rs +static mut P: *mut usize = std::ptr::null_mut(); + +fn main() { + unsafe { + { + let mut x = 0; + P = &mut x; + } + std::ptr::write_volatile(P, 123); + } +} +$ rustc -Zsanitizer=address b.rs +$./b +================================================================= +==424427==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fff67be6be0 at pc 0x5647a3ea4658 bp 0x7fff67be6b90 sp 0x7fff67be6b88 +WRITE of size 8 at 0x7fff67be6be0 thread T0 + #0 0x5647a3ea4657 in core::ptr::write_volatile::h4b04601757d0376d (/tmp/b+0xb8657) + #1 0x5647a3ea4432 in b::main::h5574a756e615c9cf (/tmp/b+0xb8432) + #2 0x5647a3ea480b in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hd57e7ee01866077e (/tmp/b+0xb880b) + #3 0x5647a3eab412 in std::panicking::try::do_call::he0421ca82dd11ba3 (.llvm.8083791802951296215) (/tmp/b+0xbf412) + #4 0x5647a3eacb26 in __rust_maybe_catch_panic (/tmp/b+0xc0b26) + #5 0x5647a3ea5b66 in std::rt::lang_start_internal::h19bc96b28f670a64 (/tmp/b+0xb9b66) + #6 0x5647a3ea4788 in std::rt::lang_start::h642d10b4b6965fb8 (/tmp/b+0xb8788) + #7 0x5647a3ea449a in main (/tmp/b+0xb849a) + #8 0x7fd1d18b3bba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba) + #9 0x5647a3df7299 in _start (/tmp/b+0xb299) + +Address 0x7fff67be6be0 is located in stack of thread T0 at offset 32 in frame + #0 0x5647a3ea433f in b::main::h5574a756e615c9cf (/tmp/b+0xb833f) + + This frame has 1 object(s): + [32, 40) 'x' <== Memory access at offset 32 is inside this variable +HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork + (longjmp and C++ exceptions *are* supported) +SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/b+0xb8657) in core::ptr::write_volatile::h4b04601757d0376d +Shadow bytes around the buggy address: + 0x10006cf74d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x10006cf74d70: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[f8]f3 f3 f3 + 0x10006cf74d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10006cf74dc0: f1 f1 f1 f1 00 f3 f3 f3 00 00 00 00 00 00 00 00 +Shadow byte legend (one shadow byte represents 8 application bytes): + Addressable: 00 + Partially addressable: 01 02 03 04 05 06 07 + Heap left redzone: fa + Freed heap region: fd + Stack left redzone: f1 + Stack mid redzone: f2 + Stack right redzone: f3 + Stack after return: f5 + Stack use after scope: f8 + Global redzone: f9 + Global init order: f6 + Poisoned by user: f7 + Container overflow: fc + Array cookie: ac + Intra object redzone: bb + ASan internal: fe + Left alloca redzone: ca + Right alloca redzone: cb + Shadow gap: cc +==424427==ABORTING +``` + ## MemorySanitizer Use of uninitialized memory. Note that we are using `-Zbuild-std` to instrument From 47fd27a637b7311c81437f138932ddb426350ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 27 Jan 2020 00:00:00 +0000 Subject: [PATCH 3/3] Move sanitizer tests to a separate directory --- src/test/ui/{sanitizer-address.rs => sanitize/address.rs} | 0 src/test/ui/{sanitize-cfg.rs => sanitize/cfg.rs} | 0 src/test/ui/{sanitizer-leak.rs => sanitize/leak.rs} | 0 src/test/ui/{sanitizer-memory.rs => sanitize/memory.rs} | 0 .../unsupported-target.rs} | 0 .../unsupported-target.stderr} | 0 .../{sanitizer-use-after-scope.rs => sanitize/use-after-scope.rs} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename src/test/ui/{sanitizer-address.rs => sanitize/address.rs} (100%) rename src/test/ui/{sanitize-cfg.rs => sanitize/cfg.rs} (100%) rename src/test/ui/{sanitizer-leak.rs => sanitize/leak.rs} (100%) rename src/test/ui/{sanitizer-memory.rs => sanitize/memory.rs} (100%) rename src/test/ui/{sanitizer-unsupported-target.rs => sanitize/unsupported-target.rs} (100%) rename src/test/ui/{sanitizer-unsupported-target.stderr => sanitize/unsupported-target.stderr} (100%) rename src/test/ui/{sanitizer-use-after-scope.rs => sanitize/use-after-scope.rs} (100%) diff --git a/src/test/ui/sanitizer-address.rs b/src/test/ui/sanitize/address.rs similarity index 100% rename from src/test/ui/sanitizer-address.rs rename to src/test/ui/sanitize/address.rs diff --git a/src/test/ui/sanitize-cfg.rs b/src/test/ui/sanitize/cfg.rs similarity index 100% rename from src/test/ui/sanitize-cfg.rs rename to src/test/ui/sanitize/cfg.rs diff --git a/src/test/ui/sanitizer-leak.rs b/src/test/ui/sanitize/leak.rs similarity index 100% rename from src/test/ui/sanitizer-leak.rs rename to src/test/ui/sanitize/leak.rs diff --git a/src/test/ui/sanitizer-memory.rs b/src/test/ui/sanitize/memory.rs similarity index 100% rename from src/test/ui/sanitizer-memory.rs rename to src/test/ui/sanitize/memory.rs diff --git a/src/test/ui/sanitizer-unsupported-target.rs b/src/test/ui/sanitize/unsupported-target.rs similarity index 100% rename from src/test/ui/sanitizer-unsupported-target.rs rename to src/test/ui/sanitize/unsupported-target.rs diff --git a/src/test/ui/sanitizer-unsupported-target.stderr b/src/test/ui/sanitize/unsupported-target.stderr similarity index 100% rename from src/test/ui/sanitizer-unsupported-target.stderr rename to src/test/ui/sanitize/unsupported-target.stderr diff --git a/src/test/ui/sanitizer-use-after-scope.rs b/src/test/ui/sanitize/use-after-scope.rs similarity index 100% rename from src/test/ui/sanitizer-use-after-scope.rs rename to src/test/ui/sanitize/use-after-scope.rs