Skip to content

Commit

Permalink
[tsan] Add support for C++ exceptions into TSan (call __tsan_func_exi…
Browse files Browse the repository at this point in the history
…t during unwinding), LLVM part

This adds support for TSan C++ exception handling, where we need to add extra calls to __tsan_func_exit when a function is exitted via exception mechanisms. Otherwise the shadow stack gets corrupted (leaked). This patch moves and enhances the existing implementation of EscapeEnumerator that finds all possible function exit points, and adds extra EH cleanup blocks where needed.

Differential Revision: https://reviews.llvm.org/D26177

llvm-svn: 286893
  • Loading branch information
kubamracek committed Nov 14, 2016
1 parent 5375fe8 commit ddfdba3
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 187 deletions.
5 changes: 5 additions & 0 deletions llvm/include/llvm/Analysis/EHPersonalities.h
Expand Up @@ -12,6 +12,7 @@

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/ErrorHandling.h"

namespace llvm {
Expand Down Expand Up @@ -39,6 +40,10 @@ enum class EHPersonality {
/// Unknown.
EHPersonality classifyEHPersonality(const Value *Pers);

StringRef getEHPersonalityName(EHPersonality Pers);

EHPersonality getDefaultEHPersonality(const Triple &T);

/// \brief Returns true if this personality function catches asynchronous
/// exceptions.
inline bool isAsynchronousEHPersonality(EHPersonality Pers) {
Expand Down
49 changes: 49 additions & 0 deletions llvm/include/llvm/Transforms/Utils/EscapeEnumerator.h
@@ -0,0 +1,49 @@
//===-- 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/IRBuilder.h"
#include "llvm/IR/Function.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.
class EscapeEnumerator {
Function &F;
const char *CleanupBBName;

Function::iterator StateBB, StateE;
IRBuilder<> Builder;
bool Done;
bool HandleExceptions;

public:
EscapeEnumerator(Function &F, const char *N = "cleanup",
bool HandleExceptions = true)
: F(F), CleanupBBName(N), StateBB(F.begin()), StateE(F.end()),
Builder(F.getContext()), Done(false),
HandleExceptions(HandleExceptions) {}

IRBuilder<> *Next();
};

}

#endif // LLVM_TRANSFORMS_UTILS_ESCAPEENUMERATOR_H
7 changes: 7 additions & 0 deletions llvm/include/llvm/Transforms/Utils/Local.h
Expand Up @@ -314,6 +314,13 @@ unsigned removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB);
/// instruction, making it and the rest of the code in the block dead.
unsigned changeToUnreachable(Instruction *I, bool UseLLVMTrap);

/// Convert the CallInst to InvokeInst with the specified unwind edge basic
/// block. This also splits the basic block where CI is located, because
/// InvokeInst is a terminator instruction. Returns the newly split basic
/// block.
BasicBlock *changeToInvokeAndSplitBasicBlock(CallInst *CI,
BasicBlock *UnwindEdge);

/// Replace 'BB's terminator with one that does not have an unwind successor
/// block. Rewrites `invoke` to `call`, etc. Updates any PHIs in unwind
/// successor.
Expand Down
23 changes: 23 additions & 0 deletions llvm/lib/Analysis/EHPersonalities.cpp
Expand Up @@ -40,6 +40,29 @@ EHPersonality llvm::classifyEHPersonality(const Value *Pers) {
.Default(EHPersonality::Unknown);
}

StringRef llvm::getEHPersonalityName(EHPersonality Pers) {
switch (Pers) {
case EHPersonality::GNU_Ada: return "__gnat_eh_personality";
case EHPersonality::GNU_CXX: return "__gxx_personality_v0";
case EHPersonality::GNU_CXX_SjLj: return "__gxx_personality_sj0";
case EHPersonality::GNU_C: return "__gcc_personality_v0";
case EHPersonality::GNU_C_SjLj: return "__gcc_personality_sj0";
case EHPersonality::GNU_ObjC: return "__objc_personality_v0";
case EHPersonality::MSVC_X86SEH: return "_except_handler3";
case EHPersonality::MSVC_Win64SEH: return "__C_specific_handler";
case EHPersonality::MSVC_CXX: return "__CxxFrameHandler3";
case EHPersonality::CoreCLR: return "ProcessCLRException";
case EHPersonality::Rust: return "rust_eh_personality";
case EHPersonality::Unknown: llvm_unreachable("Unknown EHPersonality!");
}

llvm_unreachable("Invalid EHPersonality!");
}

EHPersonality llvm::getDefaultEHPersonality(const Triple &T) {
return EHPersonality::GNU_C;
}

bool llvm::canSimplifyInvokeNoUnwind(const Function *F) {
EHPersonality Personality = classifyEHPersonality(F->getPersonalityFn());
// We can't simplify any invokes to nounwind functions if the personality
Expand Down
116 changes: 1 addition & 115 deletions llvm/lib/CodeGen/ShadowStackGCLowering.cpp
Expand Up @@ -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;

Expand Down Expand Up @@ -81,121 +82,6 @@ ShadowStackGCLowering::ShadowStackGCLowering()
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<ReturnInst>(TI) && !isa<ResumeInst>(TI))
continue;

Builder.SetInsertPoint(TI);
return &Builder;
}

State = 2;

// Find all 'call' instructions.
SmallVector<Instruction *, 16> 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<CallInst>(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<Value *, 16> Args;
for (unsigned I = Calls.size(); I != 0;) {
CallInst *CI = cast<CallInst>(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());
Expand Down

0 comments on commit ddfdba3

Please sign in to comment.