diff --git a/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 150868d5776..ce5d43a68fc 100644 --- a/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -60,6 +60,7 @@ static const char *kAsanReportErrorTemplate = "__asan_report_"; static const char *kAsanRegisterGlobalsName = "__asan_register_globals"; static const char *kAsanUnregisterGlobalsName = "__asan_unregister_globals"; static const char *kAsanInitName = "__asan_init"; +static const char *kAsanHandleNoReturnName = "__asan_handle_no_return"; static const char *kAsanMappingOffsetName = "__asan_mapping_offset"; static const char *kAsanMappingScaleName = "__asan_mapping_scale"; static const char *kAsanStackMallocName = "__asan_stack_malloc"; @@ -649,6 +650,7 @@ bool AddressSanitizer::handleFunction(Module &M, Function &F) { // (unless there are calls between uses). SmallSet TempsToInstrument; SmallVector ToInstrument; + SmallVector NoReturnCalls; // Fill the set of memory operations to instrument. for (Function::iterator FI = F.begin(), FE = F.end(); @@ -667,9 +669,12 @@ bool AddressSanitizer::handleFunction(Module &M, Function &F) { } else if (isa(BI) && ClMemIntrin) { // ok, take it. } else { - if (isa(BI)) { + if (CallInst *CI = dyn_cast(BI)) { // A call inside BB. TempsToInstrument.clear(); + if (CI->doesNotReturn()) { + NoReturnCalls.push_back(CI); + } } continue; } @@ -694,7 +699,17 @@ bool AddressSanitizer::handleFunction(Module &M, Function &F) { DEBUG(dbgs() << F); bool ChangedStack = poisonStackInFunction(M, F); - return NumInstrumented > 0 || ChangedStack; + + // We must unpoison the stack before every NoReturn call (throw, _exit, etc). + // See e.g. http://code.google.com/p/address-sanitizer/issues/detail?id=37 + for (size_t i = 0, n = NoReturnCalls.size(); i != n; i++) { + Instruction *CI = NoReturnCalls[i]; + IRBuilder<> IRB(CI); + IRB.CreateCall(M.getOrInsertFunction(kAsanHandleNoReturnName, + IRB.getVoidTy(), NULL)); + } + + return NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty(); } static uint64_t ValueForPoison(uint64_t PoisonByte, size_t ShadowRedzoneSize) { diff --git a/test/Instrumentation/AddressSanitizer/instrument-no-return.ll b/test/Instrumentation/AddressSanitizer/instrument-no-return.ll new file mode 100644 index 00000000000..80f1b1c74cd --- /dev/null +++ b/test/Instrumentation/AddressSanitizer/instrument-no-return.ll @@ -0,0 +1,17 @@ +; RUN: opt < %s -asan -S | FileCheck %s +; AddressSanitizer must insert __asan_handle_no_return +; before every noreturn call. + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" +target triple = "x86_64-unknown-linux-gnu" + +declare void @MyNoReturnFunc(i32) noreturn + +define i32 @_Z5ChildPv(i8* nocapture %arg) uwtable address_safety { +entry: + call void @MyNoReturnFunc(i32 1) noreturn + unreachable +} + +; CHECK: call void @__asan_handle_no_return +; CHECK-NEXT: call void @MyNoReturnFunc