#include "FunctionBlackList.h"
#include "llvm/Function.h"
#include "llvm/IRBuilder.h"
+#include "llvm/InlineAsm.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
namespace {
+/// When the crash callbacks are merged, they receive some amount of arguments
+/// that are merged in a PHI node. This struct represents arguments from one
+/// call site.
+struct CrashArg {
+ Value *Arg1;
+ Value *Arg2;
+};
+
/// An object of this type is created while instrumenting every function.
struct AsanFunctionContext {
- AsanFunctionContext() {
- memset(this, 0, sizeof(*this));
- }
+ AsanFunctionContext(Function &Function) : F(Function), CrashBlock() { }
+ Function &F;
// These are initially zero. If we require at least one call to
// __asan_report_{read,write}{1,2,4,8,16}, an appropriate BB is created.
BasicBlock *CrashBlock[2][kNumberOfAccessSizes];
+ typedef SmallVector<CrashArg, 8> CrashArgsVec;
+ CrashArgsVec CrashArgs[2][kNumberOfAccessSizes];
};
/// AddressSanitizer: instrument the code in module to find memory bugs.
Value *Addr, uint32_t TypeSize, bool IsWrite);
Value *createSlowPathCmp(IRBuilder<> &IRB, Value *AddrLong,
Value *ShadowValue, uint32_t TypeSize);
- Instruction *generateCrashCode(BasicBlock *BB, Value *Addr,
- bool IsWrite, uint32_t TypeSize);
+ Instruction *generateCrashCode(BasicBlock *BB, Value *Addr, Value *PC,
+ bool IsWrite, size_t AccessSizeIndex);
bool instrumentMemIntrinsic(AsanFunctionContext &AFC, MemIntrinsic *MI);
void instrumentMemIntrinsicParam(AsanFunctionContext &AFC,
Instruction *OrigIns, Value *Addr,
OwningPtr<FunctionBlackList> BL;
// This array is indexed by AccessIsWrite and log2(AccessSize).
Function *AsanErrorCallback[2][kNumberOfAccessSizes];
+ InlineAsm *EmptyAsm;
};
} // namespace
return "AddressSanitizer";
}
+static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
+ size_t Res = CountTrailingZeros_32(TypeSize / 8);
+ assert(Res < kNumberOfAccessSizes);
+ return Res;
+}
+
// Create a constant for Str so that we can pass it to the run-time lib.
static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str) {
Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
// Tail
//
// If ThenBlock is zero, a new block is created and its terminator is returned.
-// Otherwize NULL is returned.
+// Otherwize 0 is returned.
static BranchInst *splitBlockAndInsertIfThen(Value *Cmp,
BasicBlock *ThenBlock = 0) {
Instruction *SplitBefore = cast<Instruction>(Cmp)->getNextNode();
BasicBlock *Head = SplitBefore->getParent();
BasicBlock *Tail = Head->splitBasicBlock(SplitBefore);
TerminatorInst *HeadOldTerm = Head->getTerminator();
- BranchInst *CheckTerm = NULL;
+ BranchInst *CheckTerm = 0;
if (!ThenBlock) {
LLVMContext &C = Head->getParent()->getParent()->getContext();
- ThenBlock = BasicBlock::Create(C, "", Head->getParent());
+ ThenBlock = BasicBlock::Create(C, "", Head->getParent(), Tail);
CheckTerm = BranchInst::Create(Tail, ThenBlock);
}
BranchInst *HeadNewTerm =
MemIntrinsic *MI) {
Value *Dst = MI->getDest();
MemTransferInst *MemTran = dyn_cast<MemTransferInst>(MI);
- Value *Src = MemTran ? MemTran->getSource() : NULL;
+ Value *Src = MemTran ? MemTran->getSource() : 0;
Value *Length = MI->getLength();
Constant *ConstLength = dyn_cast<Constant>(Length);
}
Instruction *AddressSanitizer::generateCrashCode(
- BasicBlock *BB, Value *Addr, bool IsWrite, uint32_t TypeSize) {
+ BasicBlock *BB, Value *Addr, Value *PC,
+ bool IsWrite, size_t AccessSizeIndex) {
IRBuilder<> IRB(BB->getFirstNonPHI());
- size_t AccessSizeIndex = CountTrailingZeros_32(TypeSize / 8);
- assert(AccessSizeIndex < kNumberOfAccessSizes);
- CallInst *Call = IRB.CreateCall(AsanErrorCallback[IsWrite][AccessSizeIndex],
- Addr);
- Call->setDoesNotReturn();
+ CallInst *Call;
+ if (PC)
+ Call = IRB.CreateCall2(AsanErrorCallback[IsWrite][AccessSizeIndex],
+ Addr, PC);
+ else
+ Call = IRB.CreateCall(AsanErrorCallback[IsWrite][AccessSizeIndex], Addr);
+ // We don't do Call->setDoesNotReturn() because the BB already has
+ // UnreachableInst at the end.
+ // This EmptyAsm is required to avoid callback merge.
+ IRB.CreateCall(EmptyAsm);
return Call;
}
-Value * AddressSanitizer::createSlowPathCmp(IRBuilder<> &IRB, Value *AddrLong,
+Value *AddressSanitizer::createSlowPathCmp(IRBuilder<> &IRB, Value *AddrLong,
Value *ShadowValue,
uint32_t TypeSize) {
size_t Granularity = 1 << MappingScale;
Value *Cmp = IRB.CreateICmpNE(ShadowValue, CmpVal);
- BasicBlock *CrashBlock = NULL;
+ BasicBlock *CrashBlock = 0;
if (ClMergeCallbacks) {
- llvm_unreachable("unimplemented yet");
+ size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
+ BasicBlock **Cached = &AFC.CrashBlock[IsWrite][AccessSizeIndex];
+ if (!*Cached) {
+ std::string BBName("crash_bb-");
+ BBName += (IsWrite ? "w-" : "r-") + itostr(1 << AccessSizeIndex);
+ BasicBlock *BB = BasicBlock::Create(*C, BBName, &AFC.F);
+ new UnreachableInst(*C, BB);
+ *Cached = BB;
+ }
+ CrashBlock = *Cached;
+ // We need to pass the PC as the second parameter to __asan_report_*.
+ // There are few problems:
+ // - Some architectures (e.g. x86_32) don't have a cheap way to get the PC.
+ // - LLVM doesn't have the appropriate intrinsic.
+ // For now, put a random number into the PC, just to allow experiments.
+ Value *PC = ConstantInt::get(IntptrTy, rand());
+ CrashArg Arg = {AddrLong, PC};
+ AFC.CrashArgs[IsWrite][AccessSizeIndex].push_back(Arg);
} else {
- CrashBlock = BasicBlock::Create(*C, "crash_bb",
- OrigIns->getParent()->getParent());
+ CrashBlock = BasicBlock::Create(*C, "crash_bb", &AFC.F);
new UnreachableInst(*C, CrashBlock);
+ size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
Instruction *Crash =
- generateCrashCode(CrashBlock, AddrLong, IsWrite, TypeSize);
+ generateCrashCode(CrashBlock, AddrLong, 0, IsWrite, AccessSizeIndex);
Crash->setDebugLoc(OrigIns->getDebugLoc());
}
size_t Granularity = 1 << MappingScale;
if (TypeSize < 8 * Granularity) {
- Instruction *CheckTerm = splitBlockAndInsertIfThen(Cmp);
+ BranchInst *CheckTerm = splitBlockAndInsertIfThen(Cmp);
+ assert(CheckTerm->isUnconditional());
+ BasicBlock *NextBB = CheckTerm->getSuccessor(0);
IRB.SetInsertPoint(CheckTerm);
Value *Cmp2 = createSlowPathCmp(IRB, AddrLong, ShadowValue, TypeSize);
- splitBlockAndInsertIfThen(Cmp2, CrashBlock);
+ BranchInst *NewTerm = BranchInst::Create(CrashBlock, NextBB, Cmp2);
+ ReplaceInstWithInst(CheckTerm, NewTerm);
} else {
splitBlockAndInsertIfThen(Cmp, CrashBlock);
}
// IsWrite and TypeSize are encoded in the function name.
std::string FunctionName = std::string(kAsanReportErrorTemplate) +
(AccessIsWrite ? "store" : "load") + itostr(1 << AccessSizeIndex);
- AsanErrorCallback[AccessIsWrite][AccessSizeIndex] = cast<Function>(
- M.getOrInsertFunction(FunctionName, IRB.getVoidTy(), IntptrTy, NULL));
+ // If we are merging crash callbacks, they have two parameters.
+ if (ClMergeCallbacks)
+ AsanErrorCallback[AccessIsWrite][AccessSizeIndex] = cast<Function>(
+ M.getOrInsertFunction(FunctionName, IRB.getVoidTy(), IntptrTy,
+ IntptrTy, NULL));
+ else
+ AsanErrorCallback[AccessIsWrite][AccessSizeIndex] = cast<Function>(
+ M.getOrInsertFunction(FunctionName, IRB.getVoidTy(), IntptrTy, NULL));
}
}
+ // We insert an empty inline asm after __asan_report* to avoid callback merge.
+ EmptyAsm = InlineAsm::get(FunctionType::get(IRB.getVoidTy(), false),
+ StringRef(""), StringRef(""),
+ /*hasSideEffects=*/true);
llvm::Triple targetTriple(M.getTargetTriple());
bool isAndroid = targetTriple.getEnvironment() == llvm::Triple::ANDROIDEABI;
}
}
- AsanFunctionContext AFC;
+ AsanFunctionContext AFC(F);
// Instrument.
int NumInstrumented = 0;
NumInstrumented++;
}
+ // Create PHI nodes and crash callbacks if we are merging crash callbacks.
+ if (NumInstrumented) {
+ for (size_t IsWrite = 0; IsWrite <= 1; IsWrite++) {
+ for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes;
+ AccessSizeIndex++) {
+ BasicBlock *BB = AFC.CrashBlock[IsWrite][AccessSizeIndex];
+ if (!BB) continue;
+ assert(ClMergeCallbacks);
+ AsanFunctionContext::CrashArgsVec &Args =
+ AFC.CrashArgs[IsWrite][AccessSizeIndex];
+ IRBuilder<> IRB(BB->getFirstNonPHI());
+ size_t n = Args.size();
+ PHINode *PN1 = IRB.CreatePHI(IntptrTy, n);
+ PHINode *PN2 = IRB.CreatePHI(IntptrTy, n);
+ // We need to match crash parameters and the predecessors.
+ for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB);
+ PI != PE; ++PI) {
+ n--;
+ PN1->addIncoming(Args[n].Arg1, *PI);
+ PN2->addIncoming(Args[n].Arg2, *PI);
+ }
+ assert(n == 0);
+ generateCrashCode(BB, PN1, PN2, IsWrite, AccessSizeIndex);
+ }
+ }
+ }
+
DEBUG(dbgs() << F);
bool ChangedStack = poisonStackInFunction(M, F);