Index: include/llvm/Transforms/Utils/EscapeEnumerator.h =================================================================== --- include/llvm/Transforms/Utils/EscapeEnumerator.h +++ include/llvm/Transforms/Utils/EscapeEnumerator.h @@ -0,0 +1,139 @@ +//===-- EscapeEnumerator.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a helper class that enumerates all possible exits from a function, +// including exception handling. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_UTILS_ESCAPEENUMERATOR_H +#define LLVM_TRANSFORMS_UTILS_ESCAPEENUMERATOR_H + +#include "llvm/IR/CallSite.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" + +namespace llvm { + +/// EscapeEnumerator - This is a little algorithm to find all escape points +/// from a function so that "finally"-style code can be inserted. In addition +/// to finding the existing return and unwind instructions, it also (if +/// necessary) transforms any call instructions into invokes and sends them to +/// a landing pad. +/// +/// It's wrapped up in a state machine using the same transform C# uses for +/// 'yield return' enumerators, This transform allows it to be non-allocating. +class EscapeEnumerator { + Function &F; + const char *CleanupBBName; + + // State. + int State; + Function::iterator StateBB, StateE; + IRBuilder<> Builder; + +public: + EscapeEnumerator(Function &F, const char *N = "cleanup") + : F(F), CleanupBBName(N), State(0), Builder(F.getContext()) {} + + IRBuilder<> *Next() { + switch (State) { + default: + return nullptr; + + case 0: + StateBB = F.begin(); + StateE = F.end(); + State = 1; + + case 1: + // Find all 'return', 'resume', and 'unwind' instructions. + while (StateBB != StateE) { + BasicBlock *CurBB = &*StateBB++; + + // Branches and invokes do not escape, only unwind, resume, and return + // do. + TerminatorInst *TI = CurBB->getTerminator(); + if (!isa(TI) && !isa(TI)) + continue; + + Builder.SetInsertPoint(TI); + return &Builder; + } + + State = 2; + + // Find all 'call' instructions. + SmallVector Calls; + for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) + for (BasicBlock::iterator II = BB->begin(), EE = BB->end(); II != EE; + ++II) + if (CallInst *CI = dyn_cast(II)) + if (!CI->getCalledFunction() || + !CI->getCalledFunction()->getIntrinsicID()) + Calls.push_back(CI); + + if (Calls.empty()) + return nullptr; + + // Create a cleanup block. + LLVMContext &C = F.getContext(); + BasicBlock *CleanupBB = BasicBlock::Create(C, CleanupBBName, &F); + Type *ExnTy = + StructType::get(Type::getInt8PtrTy(C), Type::getInt32Ty(C), nullptr); + if (!F.hasPersonalityFn()) { + Constant *PersFn = F.getParent()->getOrInsertFunction( + "__gcc_personality_v0", + FunctionType::get(Type::getInt32Ty(C), true)); + F.setPersonalityFn(PersFn); + } + LandingPadInst *LPad = + LandingPadInst::Create(ExnTy, 1, "cleanup.lpad", CleanupBB); + LPad->setCleanup(true); + ResumeInst *RI = ResumeInst::Create(LPad, CleanupBB); + + // Transform the 'call' instructions into 'invoke's branching to the + // cleanup block. Go in reverse order to make prettier BB names. + SmallVector Args; + for (unsigned I = Calls.size(); I != 0;) { + CallInst *CI = cast(Calls[--I]); + + // Split the basic block containing the function call. + BasicBlock *CallBB = CI->getParent(); + BasicBlock *NewBB = CallBB->splitBasicBlock( + CI->getIterator(), CallBB->getName() + ".cont"); + + // Remove the unconditional branch inserted at the end of CallBB. + CallBB->getInstList().pop_back(); + NewBB->getInstList().remove(CI); + + // Create a new invoke instruction. + Args.clear(); + CallSite CS(CI); + Args.append(CS.arg_begin(), CS.arg_end()); + + InvokeInst *II = + InvokeInst::Create(CI->getCalledValue(), NewBB, CleanupBB, Args, + CI->getName(), CallBB); + II->setCallingConv(CI->getCallingConv()); + II->setAttributes(CI->getAttributes()); + II->setDebugLoc(CI->getDebugLoc()); + CI->replaceAllUsesWith(II); + delete CI; + } + + Builder.SetInsertPoint(RI); + return &Builder; + } + } +}; + +} + +#endif // LLVM_TRANSFORMS_UTILS_ESCAPEENUMERATOR_H Index: lib/CodeGen/ShadowStackGCLowering.cpp =================================================================== --- lib/CodeGen/ShadowStackGCLowering.cpp +++ lib/CodeGen/ShadowStackGCLowering.cpp @@ -23,6 +23,7 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" +#include "llvm/Transforms/Utils/EscapeEnumerator.h" using namespace llvm; @@ -81,121 +82,6 @@ initializeShadowStackGCLoweringPass(*PassRegistry::getPassRegistry()); } -namespace { -/// EscapeEnumerator - This is a little algorithm to find all escape points -/// from a function so that "finally"-style code can be inserted. In addition -/// to finding the existing return and unwind instructions, it also (if -/// necessary) transforms any call instructions into invokes and sends them to -/// a landing pad. -/// -/// It's wrapped up in a state machine using the same transform C# uses for -/// 'yield return' enumerators, This transform allows it to be non-allocating. -class EscapeEnumerator { - Function &F; - const char *CleanupBBName; - - // State. - int State; - Function::iterator StateBB, StateE; - IRBuilder<> Builder; - -public: - EscapeEnumerator(Function &F, const char *N = "cleanup") - : F(F), CleanupBBName(N), State(0), Builder(F.getContext()) {} - - IRBuilder<> *Next() { - switch (State) { - default: - return nullptr; - - case 0: - StateBB = F.begin(); - StateE = F.end(); - State = 1; - - case 1: - // Find all 'return', 'resume', and 'unwind' instructions. - while (StateBB != StateE) { - BasicBlock *CurBB = &*StateBB++; - - // Branches and invokes do not escape, only unwind, resume, and return - // do. - TerminatorInst *TI = CurBB->getTerminator(); - if (!isa(TI) && !isa(TI)) - continue; - - Builder.SetInsertPoint(TI); - return &Builder; - } - - State = 2; - - // Find all 'call' instructions. - SmallVector Calls; - for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) - for (BasicBlock::iterator II = BB->begin(), EE = BB->end(); II != EE; - ++II) - if (CallInst *CI = dyn_cast(II)) - if (!CI->getCalledFunction() || - !CI->getCalledFunction()->getIntrinsicID()) - Calls.push_back(CI); - - if (Calls.empty()) - return nullptr; - - // Create a cleanup block. - LLVMContext &C = F.getContext(); - BasicBlock *CleanupBB = BasicBlock::Create(C, CleanupBBName, &F); - Type *ExnTy = - StructType::get(Type::getInt8PtrTy(C), Type::getInt32Ty(C), nullptr); - if (!F.hasPersonalityFn()) { - Constant *PersFn = F.getParent()->getOrInsertFunction( - "__gcc_personality_v0", - FunctionType::get(Type::getInt32Ty(C), true)); - F.setPersonalityFn(PersFn); - } - LandingPadInst *LPad = - LandingPadInst::Create(ExnTy, 1, "cleanup.lpad", CleanupBB); - LPad->setCleanup(true); - ResumeInst *RI = ResumeInst::Create(LPad, CleanupBB); - - // Transform the 'call' instructions into 'invoke's branching to the - // cleanup block. Go in reverse order to make prettier BB names. - SmallVector Args; - for (unsigned I = Calls.size(); I != 0;) { - CallInst *CI = cast(Calls[--I]); - - // Split the basic block containing the function call. - BasicBlock *CallBB = CI->getParent(); - BasicBlock *NewBB = CallBB->splitBasicBlock( - CI->getIterator(), CallBB->getName() + ".cont"); - - // Remove the unconditional branch inserted at the end of CallBB. - CallBB->getInstList().pop_back(); - NewBB->getInstList().remove(CI); - - // Create a new invoke instruction. - Args.clear(); - CallSite CS(CI); - Args.append(CS.arg_begin(), CS.arg_end()); - - InvokeInst *II = - InvokeInst::Create(CI->getCalledValue(), NewBB, CleanupBB, Args, - CI->getName(), CallBB); - II->setCallingConv(CI->getCallingConv()); - II->setAttributes(CI->getAttributes()); - CI->replaceAllUsesWith(II); - delete CI; - } - - Builder.SetInsertPoint(RI); - return &Builder; - } - } -}; -} - - Constant *ShadowStackGCLowering::GetFrameMap(Function &F) { // doInitialization creates the abstract type of this value. Type *VoidPtr = Type::getInt8PtrTy(F.getContext()); Index: lib/Transforms/Instrumentation/ThreadSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/ThreadSanitizer.cpp +++ lib/Transforms/Instrumentation/ThreadSanitizer.cpp @@ -43,6 +43,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/EscapeEnumerator.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ModuleUtils.h" @@ -382,7 +383,6 @@ if (&F == TsanCtorFunction) return false; initializeCallbacks(*F.getParent()); - SmallVector RetVec; SmallVector AllLoadsAndStores; SmallVector LocalLoadsAndStores; SmallVector AtomicAccesses; @@ -401,8 +401,6 @@ AtomicAccesses.push_back(&Inst); else if (isa(Inst) || isa(Inst)) LocalLoadsAndStores.push_back(&Inst); - else if (isa(Inst)) - RetVec.push_back(&Inst); else if (isa(Inst) || isa(Inst)) { if (CallInst *CI = dyn_cast(&Inst)) maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); @@ -445,9 +443,10 @@ Intrinsic::getDeclaration(F.getParent(), Intrinsic::returnaddress), IRB.getInt32(0)); IRB.CreateCall(TsanFuncEntry, ReturnAddress); - for (auto RetInst : RetVec) { - IRBuilder<> IRBRet(RetInst); - IRBRet.CreateCall(TsanFuncExit, {}); + + EscapeEnumerator EE(F, "tsan_cleanup"); + while (IRBuilder<> *AtExit = EE.Next()) { + AtExit->CreateCall(TsanFuncExit, {}); } Res = true; } Index: projects/compiler-rt/lib/tsan/rtl/tsan_rtl.cc =================================================================== --- projects/compiler-rt/lib/tsan/rtl/tsan_rtl.cc +++ projects/compiler-rt/lib/tsan/rtl/tsan_rtl.cc @@ -1002,6 +1002,14 @@ } } +#if !SANITIZER_GO +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +uptr __tsan_testonly_shadow_stack_current_size() { + ThreadState *thr = cur_thread(); + return thr->shadow_stack_pos - thr->shadow_stack; +} +#endif + void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); thr->ignore_sync++; Index: projects/compiler-rt/test/tsan/exceptions.cc =================================================================== --- projects/compiler-rt/test/tsan/exceptions.cc +++ projects/compiler-rt/test/tsan/exceptions.cc @@ -0,0 +1,135 @@ +// RUN: %clangxx_tsan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include "test.h" + +__attribute__((noinline)) void throws_int() { + throw 42; +} + +__attribute__((noinline)) void callee_throws() { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "callee_throws caught exception\n"); + } +} + +__attribute__((noinline)) void throws_catches_rethrows() { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "throws_catches_rethrows caught exception\n"); + throw; + } +} + +__attribute__((noinline)) void callee_rethrows() { + try { + throws_catches_rethrows(); + } catch (int) { // NOLINT + fprintf(stderr, "callee_rethrows caught exception\n"); + } +} + +__attribute__((noinline)) void throws_and_catches() { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "throws_and_catches caught exception\n"); + } +} + +__attribute__((noinline)) void nested_try() { + try { + try { + throws_int(); + } catch (double) { // NOLINT + fprintf(stderr, "nested_try inner block caught exception\n"); + } + } catch (int) { // NOLINT + fprintf(stderr, "nested_try outer block caught exception\n"); + } +} + +__attribute__((noinline)) void nested_try2() { + try { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "nested_try inner block caught exception\n"); + } + } catch (double) { // NOLINT + fprintf(stderr, "nested_try outer block caught exception\n"); + } +} + +class ClassWithDestructor { + public: + ClassWithDestructor() { + fprintf(stderr, "ClassWithDestructor\n"); + } + ~ClassWithDestructor() { + fprintf(stderr, "~ClassWithDestructor\n"); + } +}; + +__attribute__((noinline)) void local_object_then_throw() { + ClassWithDestructor obj; + throws_int(); +} + +__attribute__((noinline)) void cpp_object_with_destructor() { + try { + local_object_then_throw(); + } catch (int) { // NOLINT + fprintf(stderr, "cpp_object_with_destructor caught exception\n"); + } +} + +__attribute__((noinline)) void recursive_call(long n) { + if (n > 0) { + recursive_call(n - 1); + } else { + throws_int(); + } +} + +__attribute__((noinline)) void multiframe_unwind() { + try { + recursive_call(5); + } catch (int) { // NOLINT + fprintf(stderr, "multiframe_unwind caught exception\n"); + } +} + +int main(int argc, const char * argv[]) { + fprintf(stderr, "Hello, World!\n"); + unsigned long shadow_stack_size = __tsan_testonly_shadow_stack_current_size(); + + throws_and_catches(); + fprintf(stderr, (shadow_stack_size == __tsan_testonly_shadow_stack_current_size() ? "OK.\n" : "Shadow stack leak!\n")); + + callee_throws(); + fprintf(stderr, (shadow_stack_size == __tsan_testonly_shadow_stack_current_size() ? "OK.\n" : "Shadow stack leak!\n")); + + callee_rethrows(); + fprintf(stderr, (shadow_stack_size == __tsan_testonly_shadow_stack_current_size() ? "OK.\n" : "Shadow stack leak!\n")); + + nested_try(); + fprintf(stderr, (shadow_stack_size == __tsan_testonly_shadow_stack_current_size() ? "OK.\n" : "Shadow stack leak!\n")); + + nested_try2(); + fprintf(stderr, (shadow_stack_size == __tsan_testonly_shadow_stack_current_size() ? "OK.\n" : "Shadow stack leak!\n")); + + cpp_object_with_destructor(); + fprintf(stderr, (shadow_stack_size == __tsan_testonly_shadow_stack_current_size() ? "OK.\n" : "Shadow stack leak!\n")); + + multiframe_unwind(); + fprintf(stderr, (shadow_stack_size == __tsan_testonly_shadow_stack_current_size() ? "OK.\n" : "Shadow stack leak!\n")); + + return 0; +} + +// CHECK: Hello, World! +// CHECK-NOT: Shadow stack leak Index: projects/compiler-rt/test/tsan/test.h =================================================================== --- projects/compiler-rt/test/tsan/test.h +++ projects/compiler-rt/test/tsan/test.h @@ -22,6 +22,7 @@ void __tsan_testonly_barrier_init(invisible_barrier_t *barrier, unsigned count); void __tsan_testonly_barrier_wait(invisible_barrier_t *barrier); +unsigned long __tsan_testonly_shadow_stack_current_size(); #ifdef __cplusplus } #endif