mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-02-26 10:29:36 +00:00
asan: do not instrument direct inbounds accesses to stack variables
Do not instrument direct accesses to stack variables that can be proven to be inbounds, e.g. accesses to fields of structs on stack. But it eliminates 33% of instrumentation on webrtc/modules_unittests (number of memory accesses goes down from 290152 to 193998) and reduces binary size by 15% (from 74M to 64M) and improved compilation time by 6-12%. The optimization is guarded by asan-opt-stack flag that is off by default. http://reviews.llvm.org/D7583 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@231241 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
dee2f990fe
commit
826cbaf934
@ -24,6 +24,9 @@
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Analysis/MemoryBuiltins.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/DIBuilder.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
@ -121,42 +124,52 @@ static const unsigned kAsanAllocaPartialVal2 = 0x000000cbU;
|
||||
|
||||
// This flag may need to be replaced with -f[no-]asan-reads.
|
||||
static cl::opt<bool> ClInstrumentReads("asan-instrument-reads",
|
||||
cl::desc("instrument read instructions"), cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClInstrumentWrites("asan-instrument-writes",
|
||||
cl::desc("instrument write instructions"), cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClInstrumentAtomics("asan-instrument-atomics",
|
||||
cl::desc("instrument atomic instructions (rmw, cmpxchg)"),
|
||||
cl::desc("instrument read instructions"),
|
||||
cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClAlwaysSlowPath("asan-always-slow-path",
|
||||
cl::desc("use instrumentation with slow path for all accesses"),
|
||||
cl::Hidden, cl::init(false));
|
||||
static cl::opt<bool> ClInstrumentWrites(
|
||||
"asan-instrument-writes", cl::desc("instrument write instructions"),
|
||||
cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClInstrumentAtomics(
|
||||
"asan-instrument-atomics",
|
||||
cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
|
||||
cl::init(true));
|
||||
static cl::opt<bool> ClAlwaysSlowPath(
|
||||
"asan-always-slow-path",
|
||||
cl::desc("use instrumentation with slow path for all accesses"), cl::Hidden,
|
||||
cl::init(false));
|
||||
// This flag limits the number of instructions to be instrumented
|
||||
// in any given BB. Normally, this should be set to unlimited (INT_MAX),
|
||||
// but due to http://llvm.org/bugs/show_bug.cgi?id=12652 we temporary
|
||||
// set it to 10000.
|
||||
static cl::opt<int> ClMaxInsnsToInstrumentPerBB("asan-max-ins-per-bb",
|
||||
cl::init(10000),
|
||||
static cl::opt<int> ClMaxInsnsToInstrumentPerBB(
|
||||
"asan-max-ins-per-bb", cl::init(10000),
|
||||
cl::desc("maximal number of instructions to instrument in any given BB"),
|
||||
cl::Hidden);
|
||||
// This flag may need to be replaced with -f[no]asan-stack.
|
||||
static cl::opt<bool> ClStack("asan-stack",
|
||||
cl::desc("Handle stack memory"), cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClStack("asan-stack", cl::desc("Handle stack memory"),
|
||||
cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClUseAfterReturn("asan-use-after-return",
|
||||
cl::desc("Check return-after-free"), cl::Hidden, cl::init(true));
|
||||
cl::desc("Check return-after-free"),
|
||||
cl::Hidden, cl::init(true));
|
||||
// This flag may need to be replaced with -f[no]asan-globals.
|
||||
static cl::opt<bool> ClGlobals("asan-globals",
|
||||
cl::desc("Handle global objects"), cl::Hidden, cl::init(true));
|
||||
cl::desc("Handle global objects"), cl::Hidden,
|
||||
cl::init(true));
|
||||
static cl::opt<bool> ClInitializers("asan-initialization-order",
|
||||
cl::desc("Handle C++ initializer order"), cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClInvalidPointerPairs("asan-detect-invalid-pointer-pair",
|
||||
cl::desc("Instrument <, <=, >, >=, - with pointer operands"),
|
||||
cl::Hidden, cl::init(false));
|
||||
static cl::opt<unsigned> ClRealignStack("asan-realign-stack",
|
||||
cl::desc("Handle C++ initializer order"),
|
||||
cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClInvalidPointerPairs(
|
||||
"asan-detect-invalid-pointer-pair",
|
||||
cl::desc("Instrument <, <=, >, >=, - with pointer operands"), cl::Hidden,
|
||||
cl::init(false));
|
||||
static cl::opt<unsigned> ClRealignStack(
|
||||
"asan-realign-stack",
|
||||
cl::desc("Realign stack to the value of this flag (power of two)"),
|
||||
cl::Hidden, cl::init(32));
|
||||
static cl::opt<int> ClInstrumentationWithCallsThreshold(
|
||||
"asan-instrumentation-with-call-threshold",
|
||||
cl::desc("If the function being instrumented contains more than "
|
||||
cl::desc(
|
||||
"If the function being instrumented contains more than "
|
||||
"this number of memory accesses, use callbacks instead of "
|
||||
"inline checks (-1 means never use callbacks)."),
|
||||
cl::Hidden, cl::init(7000));
|
||||
@ -165,31 +178,39 @@ static cl::opt<std::string> ClMemoryAccessCallbackPrefix(
|
||||
cl::desc("Prefix for memory access callbacks"), cl::Hidden,
|
||||
cl::init("__asan_"));
|
||||
static cl::opt<bool> ClInstrumentAllocas("asan-instrument-allocas",
|
||||
cl::desc("instrument dynamic allocas"), cl::Hidden, cl::init(false));
|
||||
static cl::opt<bool> ClSkipPromotableAllocas("asan-skip-promotable-allocas",
|
||||
cl::desc("Do not instrument promotable allocas"),
|
||||
cl::Hidden, cl::init(true));
|
||||
cl::desc("instrument dynamic allocas"),
|
||||
cl::Hidden, cl::init(false));
|
||||
static cl::opt<bool> ClSkipPromotableAllocas(
|
||||
"asan-skip-promotable-allocas",
|
||||
cl::desc("Do not instrument promotable allocas"), cl::Hidden,
|
||||
cl::init(true));
|
||||
|
||||
// These flags allow to change the shadow mapping.
|
||||
// The shadow mapping looks like
|
||||
// Shadow = (Mem >> scale) + (1 << offset_log)
|
||||
static cl::opt<int> ClMappingScale("asan-mapping-scale",
|
||||
cl::desc("scale of asan shadow mapping"), cl::Hidden, cl::init(0));
|
||||
cl::desc("scale of asan shadow mapping"),
|
||||
cl::Hidden, cl::init(0));
|
||||
|
||||
// Optimization flags. Not user visible, used mostly for testing
|
||||
// and benchmarking the tool.
|
||||
static cl::opt<bool> ClOpt("asan-opt",
|
||||
cl::desc("Optimize instrumentation"), cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClOptSameTemp("asan-opt-same-temp",
|
||||
cl::desc("Instrument the same temp just once"), cl::Hidden,
|
||||
cl::init(true));
|
||||
static cl::opt<bool> ClOpt("asan-opt", cl::desc("Optimize instrumentation"),
|
||||
cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClOptSameTemp(
|
||||
"asan-opt-same-temp", cl::desc("Instrument the same temp just once"),
|
||||
cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClOptGlobals("asan-opt-globals",
|
||||
cl::desc("Don't instrument scalar globals"), cl::Hidden, cl::init(true));
|
||||
|
||||
static cl::opt<bool> ClCheckLifetime("asan-check-lifetime",
|
||||
cl::desc("Use llvm.lifetime intrinsics to insert extra checks"),
|
||||
cl::desc("Don't instrument scalar globals"),
|
||||
cl::Hidden, cl::init(true));
|
||||
static cl::opt<bool> ClOptStack(
|
||||
"asan-opt-stack", cl::desc("Don't instrument scalar stack variables"),
|
||||
cl::Hidden, cl::init(false));
|
||||
|
||||
static cl::opt<bool> ClCheckLifetime(
|
||||
"asan-check-lifetime",
|
||||
cl::desc("Use llvm.lifetime intrinsics to insert extra checks"), cl::Hidden,
|
||||
cl::init(false));
|
||||
|
||||
static cl::opt<bool> ClDynamicAllocaStack(
|
||||
"asan-stack-dynamic-alloca",
|
||||
cl::desc("Use dynamic alloca to represent stack variables"), cl::Hidden,
|
||||
@ -200,8 +221,8 @@ static cl::opt<int> ClDebug("asan-debug", cl::desc("debug"), cl::Hidden,
|
||||
cl::init(0));
|
||||
static cl::opt<int> ClDebugStack("asan-debug-stack", cl::desc("debug stack"),
|
||||
cl::Hidden, cl::init(0));
|
||||
static cl::opt<std::string> ClDebugFunc("asan-debug-func",
|
||||
cl::Hidden, cl::desc("Debug func"));
|
||||
static cl::opt<std::string> ClDebugFunc("asan-debug-func", cl::Hidden,
|
||||
cl::desc("Debug func"));
|
||||
static cl::opt<int> ClDebugMin("asan-debug-min", cl::desc("Debug min inst"),
|
||||
cl::Hidden, cl::init(-1));
|
||||
static cl::opt<int> ClDebugMax("asan-debug-max", cl::desc("Debug man inst"),
|
||||
@ -211,10 +232,10 @@ STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
|
||||
STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
|
||||
STATISTIC(NumInstrumentedDynamicAllocas,
|
||||
"Number of instrumented dynamic allocas");
|
||||
STATISTIC(NumOptimizedAccessesToGlobalArray,
|
||||
"Number of optimized accesses to global arrays");
|
||||
STATISTIC(NumOptimizedAccessesToGlobalVar,
|
||||
"Number of optimized accesses to global vars");
|
||||
STATISTIC(NumOptimizedAccessesToStackVar,
|
||||
"Number of optimized accesses to stack vars");
|
||||
|
||||
namespace {
|
||||
/// Frontend-provided metadata for source location.
|
||||
@ -242,9 +263,7 @@ struct LocationMetadata {
|
||||
class GlobalsMetadata {
|
||||
public:
|
||||
struct Entry {
|
||||
Entry()
|
||||
: SourceLoc(), Name(), IsDynInit(false),
|
||||
IsBlacklisted(false) {}
|
||||
Entry() : SourceLoc(), Name(), IsDynInit(false), IsBlacklisted(false) {}
|
||||
LocationMetadata SourceLoc;
|
||||
StringRef Name;
|
||||
bool IsDynInit;
|
||||
@ -257,15 +276,13 @@ class GlobalsMetadata {
|
||||
assert(!inited_);
|
||||
inited_ = true;
|
||||
NamedMDNode *Globals = M.getNamedMetadata("llvm.asan.globals");
|
||||
if (!Globals)
|
||||
return;
|
||||
if (!Globals) return;
|
||||
for (auto MDN : Globals->operands()) {
|
||||
// Metadata node contains the global and the fields of "Entry".
|
||||
assert(MDN->getNumOperands() == 5);
|
||||
auto *GV = mdconst::extract_or_null<GlobalVariable>(MDN->getOperand(0));
|
||||
// The optimizer may optimize away a global entirely.
|
||||
if (!GV)
|
||||
continue;
|
||||
if (!GV) continue;
|
||||
// We can already have an entry for GV if it was merged with another
|
||||
// global.
|
||||
Entry &E = Entries[GV];
|
||||
@ -375,6 +392,8 @@ struct AddressSanitizer : public FunctionPass {
|
||||
}
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
AU.addRequired<DataLayoutPass>();
|
||||
AU.addRequired<TargetLibraryInfoWrapperPass>();
|
||||
}
|
||||
uint64_t getAllocaSizeInBytes(AllocaInst *AI) const {
|
||||
Type *Ty = AI->getAllocatedType();
|
||||
@ -386,8 +405,10 @@ struct AddressSanitizer : public FunctionPass {
|
||||
/// If it is an interesting memory access, return the PointerOperand
|
||||
/// and set IsWrite/Alignment. Otherwise return nullptr.
|
||||
Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite,
|
||||
uint64_t *TypeSize,
|
||||
unsigned *Alignment) const;
|
||||
void instrumentMop(Instruction *I, bool UseCalls);
|
||||
void instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis, Instruction *I,
|
||||
bool UseCalls);
|
||||
void instrumentPointerComparisonOrSubtraction(Instruction *I);
|
||||
void instrumentAddress(Instruction *OrigIns, Instruction *InsertBefore,
|
||||
Value *Addr, uint32_t TypeSize, bool IsWrite,
|
||||
@ -411,6 +432,8 @@ struct AddressSanitizer : public FunctionPass {
|
||||
|
||||
bool LooksLikeCodeInBug11395(Instruction *I);
|
||||
bool GlobalIsLinkerInitialized(GlobalVariable *G);
|
||||
bool isSafeAccess(ObjectSizeOffsetVisitor &ObjSizeVis, Value *Addr,
|
||||
uint64_t TypeSize) const;
|
||||
|
||||
LLVMContext *C;
|
||||
const DataLayout *DL;
|
||||
@ -427,8 +450,7 @@ struct AddressSanitizer : public FunctionPass {
|
||||
Function *AsanErrorCallback[2][kNumberOfAccessSizes];
|
||||
Function *AsanMemoryAccessCallback[2][kNumberOfAccessSizes];
|
||||
// This array is indexed by AccessIsWrite.
|
||||
Function *AsanErrorCallbackSized[2],
|
||||
*AsanMemoryAccessCallbackSized[2];
|
||||
Function *AsanErrorCallbackSized[2], *AsanMemoryAccessCallbackSized[2];
|
||||
Function *AsanMemmove, *AsanMemcpy, *AsanMemset;
|
||||
InlineAsm *EmptyAsm;
|
||||
GlobalsMetadata GlobalsMD;
|
||||
@ -441,9 +463,7 @@ class AddressSanitizerModule : public ModulePass {
|
||||
AddressSanitizerModule() : ModulePass(ID) {}
|
||||
bool runOnModule(Module &M) override;
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
const char *getPassName() const override {
|
||||
return "AddressSanitizerModule";
|
||||
}
|
||||
const char *getPassName() const override { return "AddressSanitizerModule"; }
|
||||
|
||||
private:
|
||||
void initializeCallbacks(Module &M);
|
||||
@ -512,11 +532,12 @@ struct FunctionStackPoisoner : public InstVisitor<FunctionStackPoisoner> {
|
||||
Value *LeftRzAddr;
|
||||
Value *RightRzAddr;
|
||||
bool Poison;
|
||||
explicit DynamicAllocaCall(AllocaInst *AI,
|
||||
Value *LeftRzAddr = nullptr,
|
||||
explicit DynamicAllocaCall(AllocaInst *AI, Value *LeftRzAddr = nullptr,
|
||||
Value *RightRzAddr = nullptr)
|
||||
: AI(AI), LeftRzAddr(LeftRzAddr), RightRzAddr(RightRzAddr), Poison(true)
|
||||
{}
|
||||
: AI(AI),
|
||||
LeftRzAddr(LeftRzAddr),
|
||||
RightRzAddr(RightRzAddr),
|
||||
Poison(true) {}
|
||||
};
|
||||
SmallVector<DynamicAllocaCall, 1> DynamicAllocaVec;
|
||||
|
||||
@ -528,17 +549,21 @@ struct FunctionStackPoisoner : public InstVisitor<FunctionStackPoisoner> {
|
||||
std::unique_ptr<CallInst> EmptyInlineAsm;
|
||||
|
||||
FunctionStackPoisoner(Function &F, AddressSanitizer &ASan)
|
||||
: F(F), ASan(ASan), DIB(*F.getParent(), /*AllowUnresolved*/ false),
|
||||
C(ASan.C), IntptrTy(ASan.IntptrTy),
|
||||
IntptrPtrTy(PointerType::get(IntptrTy, 0)), Mapping(ASan.Mapping),
|
||||
StackAlignment(1 << Mapping.Scale), HasNonEmptyInlineAsm(false),
|
||||
: F(F),
|
||||
ASan(ASan),
|
||||
DIB(*F.getParent(), /*AllowUnresolved*/ false),
|
||||
C(ASan.C),
|
||||
IntptrTy(ASan.IntptrTy),
|
||||
IntptrPtrTy(PointerType::get(IntptrTy, 0)),
|
||||
Mapping(ASan.Mapping),
|
||||
StackAlignment(1 << Mapping.Scale),
|
||||
HasNonEmptyInlineAsm(false),
|
||||
EmptyInlineAsm(CallInst::Create(ASan.EmptyAsm)) {}
|
||||
|
||||
bool runOnFunction() {
|
||||
if (!ClStack) return false;
|
||||
// Collect alloca, ret, lifetime instructions etc.
|
||||
for (BasicBlock *BB : depth_first(&F.getEntryBlock()))
|
||||
visit(*BB);
|
||||
for (BasicBlock *BB : depth_first(&F.getEntryBlock())) visit(*BB);
|
||||
|
||||
if (AllocaVec.empty() && DynamicAllocaVec.empty()) return false;
|
||||
|
||||
@ -559,26 +584,23 @@ struct FunctionStackPoisoner : public InstVisitor<FunctionStackPoisoner> {
|
||||
|
||||
// ----------------------- Visitors.
|
||||
/// \brief Collect all Ret instructions.
|
||||
void visitReturnInst(ReturnInst &RI) {
|
||||
RetVec.push_back(&RI);
|
||||
}
|
||||
void visitReturnInst(ReturnInst &RI) { RetVec.push_back(&RI); }
|
||||
|
||||
// Unpoison dynamic allocas redzones.
|
||||
void unpoisonDynamicAlloca(DynamicAllocaCall &AllocaCall) {
|
||||
if (!AllocaCall.Poison)
|
||||
return;
|
||||
if (!AllocaCall.Poison) return;
|
||||
for (auto Ret : RetVec) {
|
||||
IRBuilder<> IRBRet(Ret);
|
||||
PointerType *Int32PtrTy = PointerType::getUnqual(IRBRet.getInt32Ty());
|
||||
Value *Zero = Constant::getNullValue(IRBRet.getInt32Ty());
|
||||
Value *PartialRzAddr = IRBRet.CreateSub(AllocaCall.RightRzAddr,
|
||||
ConstantInt::get(IntptrTy, 4));
|
||||
IRBRet.CreateStore(Zero, IRBRet.CreateIntToPtr(AllocaCall.LeftRzAddr,
|
||||
Int32PtrTy));
|
||||
IRBRet.CreateStore(Zero, IRBRet.CreateIntToPtr(PartialRzAddr,
|
||||
Int32PtrTy));
|
||||
IRBRet.CreateStore(Zero, IRBRet.CreateIntToPtr(AllocaCall.RightRzAddr,
|
||||
Int32PtrTy));
|
||||
IRBRet.CreateStore(
|
||||
Zero, IRBRet.CreateIntToPtr(AllocaCall.LeftRzAddr, Int32PtrTy));
|
||||
IRBRet.CreateStore(Zero,
|
||||
IRBRet.CreateIntToPtr(PartialRzAddr, Int32PtrTy));
|
||||
IRBRet.CreateStore(
|
||||
Zero, IRBRet.CreateIntToPtr(AllocaCall.RightRzAddr, Int32PtrTy));
|
||||
}
|
||||
}
|
||||
|
||||
@ -628,8 +650,7 @@ struct FunctionStackPoisoner : public InstVisitor<FunctionStackPoisoner> {
|
||||
void visitIntrinsicInst(IntrinsicInst &II) {
|
||||
if (!ClCheckLifetime) return;
|
||||
Intrinsic::ID ID = II.getIntrinsicID();
|
||||
if (ID != Intrinsic::lifetime_start &&
|
||||
ID != Intrinsic::lifetime_end)
|
||||
if (ID != Intrinsic::lifetime_start && ID != Intrinsic::lifetime_end)
|
||||
return;
|
||||
// Found lifetime intrinsic, add ASan instrumentation if necessary.
|
||||
ConstantInt *Size = dyn_cast<ConstantInt>(II.getArgOperand(0));
|
||||
@ -659,8 +680,7 @@ struct FunctionStackPoisoner : public InstVisitor<FunctionStackPoisoner> {
|
||||
|
||||
bool doesDominateAllExits(const Instruction *I) const {
|
||||
for (auto Ret : RetVec) {
|
||||
if (!ASan.getDominatorTree().dominates(I, Ret))
|
||||
return false;
|
||||
if (!ASan.getDominatorTree().dominates(I, Ret)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -685,21 +705,25 @@ struct FunctionStackPoisoner : public InstVisitor<FunctionStackPoisoner> {
|
||||
} // namespace
|
||||
|
||||
char AddressSanitizer::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(AddressSanitizer, "asan",
|
||||
"AddressSanitizer: detects use-after-free and out-of-bounds bugs.",
|
||||
false, false)
|
||||
INITIALIZE_PASS_BEGIN(
|
||||
AddressSanitizer, "asan",
|
||||
"AddressSanitizer: detects use-after-free and out-of-bounds bugs.", false,
|
||||
false)
|
||||
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
|
||||
INITIALIZE_PASS_END(AddressSanitizer, "asan",
|
||||
"AddressSanitizer: detects use-after-free and out-of-bounds bugs.",
|
||||
false, false)
|
||||
INITIALIZE_PASS_END(
|
||||
AddressSanitizer, "asan",
|
||||
"AddressSanitizer: detects use-after-free and out-of-bounds bugs.", false,
|
||||
false)
|
||||
FunctionPass *llvm::createAddressSanitizerFunctionPass() {
|
||||
return new AddressSanitizer();
|
||||
}
|
||||
|
||||
char AddressSanitizerModule::ID = 0;
|
||||
INITIALIZE_PASS(AddressSanitizerModule, "asan-module",
|
||||
INITIALIZE_PASS(
|
||||
AddressSanitizerModule, "asan-module",
|
||||
"AddressSanitizer: detects use-after-free and out-of-bounds bugs."
|
||||
"ModulePass", false, false)
|
||||
"ModulePass",
|
||||
false, false)
|
||||
ModulePass *llvm::createAddressSanitizerModulePass() {
|
||||
return new AddressSanitizerModule();
|
||||
}
|
||||
@ -711,16 +735,15 @@ static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
|
||||
}
|
||||
|
||||
// \brief Create a constant for Str so that we can pass it to the run-time lib.
|
||||
static GlobalVariable *createPrivateGlobalForString(
|
||||
Module &M, StringRef Str, bool AllowMerging) {
|
||||
static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str,
|
||||
bool AllowMerging) {
|
||||
Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
|
||||
// We use private linkage for module-local strings. If they can be merged
|
||||
// with another one, we set the unnamed_addr attribute.
|
||||
GlobalVariable *GV =
|
||||
new GlobalVariable(M, StrConst->getType(), true,
|
||||
GlobalValue::PrivateLinkage, StrConst, kAsanGenPrefix);
|
||||
if (AllowMerging)
|
||||
GV->setUnnamedAddr(true);
|
||||
if (AllowMerging) GV->setUnnamedAddr(true);
|
||||
GV->setAlignment(1); // Strings may not be merged w/o setting align 1.
|
||||
return GV;
|
||||
}
|
||||
@ -749,8 +772,7 @@ static bool GlobalWasGeneratedByAsan(GlobalVariable *G) {
|
||||
Value *AddressSanitizer::memToShadow(Value *Shadow, IRBuilder<> &IRB) {
|
||||
// Shadow >> scale
|
||||
Shadow = IRB.CreateLShr(Shadow, Mapping.Scale);
|
||||
if (Mapping.Offset == 0)
|
||||
return Shadow;
|
||||
if (Mapping.Offset == 0) return Shadow;
|
||||
// (Shadow >> scale) | offset
|
||||
if (Mapping.OrShadowOffset)
|
||||
return IRB.CreateOr(Shadow, ConstantInt::get(IntptrTy, Mapping.Offset));
|
||||
@ -791,30 +813,35 @@ bool AddressSanitizer::isInterestingAlloca(AllocaInst &AI) const {
|
||||
/// and set IsWrite/Alignment. Otherwise return nullptr.
|
||||
Value *AddressSanitizer::isInterestingMemoryAccess(Instruction *I,
|
||||
bool *IsWrite,
|
||||
uint64_t *TypeSize,
|
||||
unsigned *Alignment) const {
|
||||
// Skip memory accesses inserted by another instrumentation.
|
||||
if (I->getMetadata("nosanitize"))
|
||||
return nullptr;
|
||||
if (I->getMetadata("nosanitize")) return nullptr;
|
||||
|
||||
Value *PtrOperand = nullptr;
|
||||
if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
|
||||
if (!ClInstrumentReads) return nullptr;
|
||||
*IsWrite = false;
|
||||
*TypeSize = DL->getTypeStoreSizeInBits(LI->getType());
|
||||
*Alignment = LI->getAlignment();
|
||||
PtrOperand = LI->getPointerOperand();
|
||||
} else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
|
||||
if (!ClInstrumentWrites) return nullptr;
|
||||
*IsWrite = true;
|
||||
*TypeSize = DL->getTypeStoreSizeInBits(SI->getValueOperand()->getType());
|
||||
*Alignment = SI->getAlignment();
|
||||
PtrOperand = SI->getPointerOperand();
|
||||
} else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
|
||||
if (!ClInstrumentAtomics) return nullptr;
|
||||
*IsWrite = true;
|
||||
*TypeSize = DL->getTypeStoreSizeInBits(RMW->getValOperand()->getType());
|
||||
*Alignment = 0;
|
||||
PtrOperand = RMW->getPointerOperand();
|
||||
} else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) {
|
||||
if (!ClInstrumentAtomics) return nullptr;
|
||||
*IsWrite = true;
|
||||
*TypeSize =
|
||||
DL->getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType());
|
||||
*Alignment = 0;
|
||||
PtrOperand = XCHG->getPointerOperand();
|
||||
}
|
||||
@ -838,11 +865,9 @@ static bool isPointerOperand(Value *V) {
|
||||
// the frontend.
|
||||
static bool isInterestingPointerComparisonOrSubtraction(Instruction *I) {
|
||||
if (ICmpInst *Cmp = dyn_cast<ICmpInst>(I)) {
|
||||
if (!Cmp->isRelational())
|
||||
return false;
|
||||
if (!Cmp->isRelational()) return false;
|
||||
} else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(I)) {
|
||||
if (BO->getOpcode() != Instruction::Sub)
|
||||
return false;
|
||||
if (BO->getOpcode() != Instruction::Sub) return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -859,8 +884,8 @@ bool AddressSanitizer::GlobalIsLinkerInitialized(GlobalVariable *G) {
|
||||
return G->hasInitializer() && !GlobalsMD.get(G).IsDynInit;
|
||||
}
|
||||
|
||||
void
|
||||
AddressSanitizer::instrumentPointerComparisonOrSubtraction(Instruction *I) {
|
||||
void AddressSanitizer::instrumentPointerComparisonOrSubtraction(
|
||||
Instruction *I) {
|
||||
IRBuilder<> IRB(I);
|
||||
Function *F = isa<ICmpInst>(I) ? AsanPtrCmpFunction : AsanPtrSubFunction;
|
||||
Value *Param[2] = {I->getOperand(0), I->getOperand(1)};
|
||||
@ -871,38 +896,34 @@ AddressSanitizer::instrumentPointerComparisonOrSubtraction(Instruction *I) {
|
||||
IRB.CreateCall2(F, Param[0], Param[1]);
|
||||
}
|
||||
|
||||
void AddressSanitizer::instrumentMop(Instruction *I, bool UseCalls) {
|
||||
void AddressSanitizer::instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis,
|
||||
Instruction *I, bool UseCalls) {
|
||||
bool IsWrite = false;
|
||||
unsigned Alignment = 0;
|
||||
Value *Addr = isInterestingMemoryAccess(I, &IsWrite, &Alignment);
|
||||
uint64_t TypeSize = 0;
|
||||
Value *Addr = isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment);
|
||||
assert(Addr);
|
||||
|
||||
if (ClOpt && ClOptGlobals) {
|
||||
if (GlobalVariable *G = dyn_cast<GlobalVariable>(Addr)) {
|
||||
// If initialization order checking is disabled, a simple access to a
|
||||
// dynamically initialized global is always valid.
|
||||
if (!ClInitializers || GlobalIsLinkerInitialized(G)) {
|
||||
GlobalVariable *G =
|
||||
dyn_cast<GlobalVariable>(GetUnderlyingObject(Addr, nullptr));
|
||||
if (G != NULL && (!ClInitializers || GlobalIsLinkerInitialized(G)) &&
|
||||
isSafeAccess(ObjSizeVis, Addr, TypeSize)) {
|
||||
NumOptimizedAccessesToGlobalVar++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ConstantExpr *CE = dyn_cast<ConstantExpr>(Addr);
|
||||
if (CE && CE->isGEPWithNoNotionalOverIndexing()) {
|
||||
if (GlobalVariable *G = dyn_cast<GlobalVariable>(CE->getOperand(0))) {
|
||||
if (CE->getOperand(1)->isNullValue() && GlobalIsLinkerInitialized(G)) {
|
||||
NumOptimizedAccessesToGlobalArray++;
|
||||
|
||||
if (ClOpt && ClOptStack) {
|
||||
// A direct inbounds access to a stack variable is always valid.
|
||||
if (isa<AllocaInst>(GetUnderlyingObject(Addr, nullptr)) &&
|
||||
isSafeAccess(ObjSizeVis, Addr, TypeSize)) {
|
||||
NumOptimizedAccessesToStackVar++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type *OrigPtrTy = Addr->getType();
|
||||
Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
|
||||
|
||||
assert(OrigTy->isSized());
|
||||
uint32_t TypeSize = DL->getTypeStoreSizeInBits(OrigTy);
|
||||
|
||||
assert((TypeSize % 8) == 0);
|
||||
|
||||
if (IsWrite)
|
||||
NumInstrumentedWrites++;
|
||||
@ -928,7 +949,7 @@ void AddressSanitizer::instrumentMop(Instruction *I, bool UseCalls) {
|
||||
} else {
|
||||
Value *LastByte = IRB.CreateIntToPtr(
|
||||
IRB.CreateAdd(AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8 - 1)),
|
||||
OrigPtrTy);
|
||||
Addr->getType());
|
||||
instrumentAddress(I, I, Addr, 8, IsWrite, Size, false);
|
||||
instrumentAddress(I, I, LastByte, 8, IsWrite, Size, false);
|
||||
}
|
||||
@ -941,15 +962,18 @@ void AddressSanitizer::instrumentMop(Instruction *I, bool UseCalls) {
|
||||
static Function *checkInterfaceFunction(Constant *FuncOrBitcast) {
|
||||
if (isa<Function>(FuncOrBitcast)) return cast<Function>(FuncOrBitcast);
|
||||
FuncOrBitcast->dump();
|
||||
report_fatal_error("trying to redefine an AddressSanitizer "
|
||||
report_fatal_error(
|
||||
"trying to redefine an AddressSanitizer "
|
||||
"interface function");
|
||||
}
|
||||
|
||||
Instruction *AddressSanitizer::generateCrashCode(
|
||||
Instruction *InsertBefore, Value *Addr,
|
||||
bool IsWrite, size_t AccessSizeIndex, Value *SizeArgument) {
|
||||
Instruction *AddressSanitizer::generateCrashCode(Instruction *InsertBefore,
|
||||
Value *Addr, bool IsWrite,
|
||||
size_t AccessSizeIndex,
|
||||
Value *SizeArgument) {
|
||||
IRBuilder<> IRB(InsertBefore);
|
||||
CallInst *Call = SizeArgument
|
||||
CallInst *Call =
|
||||
SizeArgument
|
||||
? IRB.CreateCall2(AsanErrorCallbackSized[IsWrite], Addr, SizeArgument)
|
||||
: IRB.CreateCall(AsanErrorCallback[IsWrite][AccessSizeIndex], Addr);
|
||||
|
||||
@ -965,15 +989,15 @@ Value *AddressSanitizer::createSlowPathCmp(IRBuilder<> &IRB, Value *AddrLong,
|
||||
uint32_t TypeSize) {
|
||||
size_t Granularity = 1 << Mapping.Scale;
|
||||
// Addr & (Granularity - 1)
|
||||
Value *LastAccessedByte = IRB.CreateAnd(
|
||||
AddrLong, ConstantInt::get(IntptrTy, Granularity - 1));
|
||||
Value *LastAccessedByte =
|
||||
IRB.CreateAnd(AddrLong, ConstantInt::get(IntptrTy, Granularity - 1));
|
||||
// (Addr & (Granularity - 1)) + size - 1
|
||||
if (TypeSize / 8 > 1)
|
||||
LastAccessedByte = IRB.CreateAdd(
|
||||
LastAccessedByte, ConstantInt::get(IntptrTy, TypeSize / 8 - 1));
|
||||
// (uint8_t) ((Addr & (Granularity-1)) + size - 1)
|
||||
LastAccessedByte = IRB.CreateIntCast(
|
||||
LastAccessedByte, ShadowValue->getType(), false);
|
||||
LastAccessedByte =
|
||||
IRB.CreateIntCast(LastAccessedByte, ShadowValue->getType(), false);
|
||||
// ((uint8_t) ((Addr & (Granularity-1)) + size - 1)) >= ShadowValue
|
||||
return IRB.CreateICmpSGE(LastAccessedByte, ShadowValue);
|
||||
}
|
||||
@ -992,13 +1016,13 @@ void AddressSanitizer::instrumentAddress(Instruction *OrigIns,
|
||||
return;
|
||||
}
|
||||
|
||||
Type *ShadowTy = IntegerType::get(
|
||||
*C, std::max(8U, TypeSize >> Mapping.Scale));
|
||||
Type *ShadowTy =
|
||||
IntegerType::get(*C, std::max(8U, TypeSize >> Mapping.Scale));
|
||||
Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
|
||||
Value *ShadowPtr = memToShadow(AddrLong, IRB);
|
||||
Value *CmpVal = Constant::getNullValue(ShadowTy);
|
||||
Value *ShadowValue = IRB.CreateLoad(
|
||||
IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
|
||||
Value *ShadowValue =
|
||||
IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
|
||||
|
||||
Value *Cmp = IRB.CreateICmpNE(ShadowValue, CmpVal);
|
||||
size_t Granularity = 1 << Mapping.Scale;
|
||||
@ -1007,9 +1031,8 @@ void AddressSanitizer::instrumentAddress(Instruction *OrigIns,
|
||||
if (ClAlwaysSlowPath || (TypeSize < 8 * Granularity)) {
|
||||
// We use branch weights for the slow path check, to indicate that the slow
|
||||
// path is rarely taken. This seems to be the case for SPEC benchmarks.
|
||||
TerminatorInst *CheckTerm =
|
||||
SplitBlockAndInsertIfThen(Cmp, InsertBefore, false,
|
||||
MDBuilder(*C).createBranchWeights(1, 100000));
|
||||
TerminatorInst *CheckTerm = SplitBlockAndInsertIfThen(
|
||||
Cmp, InsertBefore, false, MDBuilder(*C).createBranchWeights(1, 100000));
|
||||
assert(dyn_cast<BranchInst>(CheckTerm)->isUnconditional());
|
||||
BasicBlock *NextBB = CheckTerm->getSuccessor(0);
|
||||
IRB.SetInsertPoint(CheckTerm);
|
||||
@ -1023,8 +1046,8 @@ void AddressSanitizer::instrumentAddress(Instruction *OrigIns,
|
||||
CrashTerm = SplitBlockAndInsertIfThen(Cmp, InsertBefore, true);
|
||||
}
|
||||
|
||||
Instruction *Crash = generateCrashCode(
|
||||
CrashTerm, AddrLong, IsWrite, AccessSizeIndex, SizeArgument);
|
||||
Instruction *Crash = generateCrashCode(CrashTerm, AddrLong, IsWrite,
|
||||
AccessSizeIndex, SizeArgument);
|
||||
Crash->setDebugLoc(OrigIns->getDebugLoc());
|
||||
}
|
||||
|
||||
@ -1049,8 +1072,7 @@ void AddressSanitizerModule::createInitializerPoisonCalls(
|
||||
|
||||
ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
|
||||
for (Use &OP : CA->operands()) {
|
||||
if (isa<ConstantAggregateZero>(OP))
|
||||
continue;
|
||||
if (isa<ConstantAggregateZero>(OP)) continue;
|
||||
ConstantStruct *CS = cast<ConstantStruct>(OP);
|
||||
|
||||
// Must have a function or null ptr.
|
||||
@ -1079,13 +1101,11 @@ bool AddressSanitizerModule::ShouldInstrumentGlobal(GlobalVariable *G) {
|
||||
G->getLinkage() != GlobalVariable::PrivateLinkage &&
|
||||
G->getLinkage() != GlobalVariable::InternalLinkage)
|
||||
return false;
|
||||
if (G->hasComdat())
|
||||
return false;
|
||||
if (G->hasComdat()) return false;
|
||||
// Two problems with thread-locals:
|
||||
// - The address of the main thread's copy can't be computed at link-time.
|
||||
// - Need to poison all copies, not just the main thread's one.
|
||||
if (G->isThreadLocal())
|
||||
return false;
|
||||
if (G->isThreadLocal()) return false;
|
||||
// For now, just ignore this Global if the alignment is large.
|
||||
if (G->getAlignment() > MinRedzoneSizeForGlobal()) return false;
|
||||
|
||||
@ -1096,10 +1116,8 @@ bool AddressSanitizerModule::ShouldInstrumentGlobal(GlobalVariable *G) {
|
||||
StringRef ParsedSegment, ParsedSection;
|
||||
unsigned TAA = 0, StubSize = 0;
|
||||
bool TAAParsed;
|
||||
std::string ErrorCode =
|
||||
MCSectionMachO::ParseSectionSpecifier(Section, ParsedSegment,
|
||||
ParsedSection, TAA, TAAParsed,
|
||||
StubSize);
|
||||
std::string ErrorCode = MCSectionMachO::ParseSectionSpecifier(
|
||||
Section, ParsedSegment, ParsedSection, TAA, TAAParsed, StubSize);
|
||||
if (!ErrorCode.empty()) {
|
||||
report_fatal_error("Invalid section specifier '" + ParsedSection +
|
||||
"': " + ErrorCode + ".");
|
||||
@ -1160,12 +1178,11 @@ void AddressSanitizerModule::initializeCallbacks(Module &M) {
|
||||
AsanUnpoisonGlobals->setLinkage(Function::ExternalLinkage);
|
||||
// Declare functions that register/unregister globals.
|
||||
AsanRegisterGlobals = checkInterfaceFunction(M.getOrInsertFunction(
|
||||
kAsanRegisterGlobalsName, IRB.getVoidTy(),
|
||||
IntptrTy, IntptrTy, nullptr));
|
||||
kAsanRegisterGlobalsName, IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr));
|
||||
AsanRegisterGlobals->setLinkage(Function::ExternalLinkage);
|
||||
AsanUnregisterGlobals = checkInterfaceFunction(M.getOrInsertFunction(
|
||||
kAsanUnregisterGlobalsName,
|
||||
IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr));
|
||||
AsanUnregisterGlobals = checkInterfaceFunction(
|
||||
M.getOrInsertFunction(kAsanUnregisterGlobalsName, IRB.getVoidTy(),
|
||||
IntptrTy, IntptrTy, nullptr));
|
||||
AsanUnregisterGlobals->setLinkage(Function::ExternalLinkage);
|
||||
}
|
||||
|
||||
@ -1178,8 +1195,7 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
|
||||
SmallVector<GlobalVariable *, 16> GlobalsToChange;
|
||||
|
||||
for (auto &G : M.globals()) {
|
||||
if (ShouldInstrumentGlobal(&G))
|
||||
GlobalsToChange.push_back(&G);
|
||||
if (ShouldInstrumentGlobal(&G)) GlobalsToChange.push_back(&G);
|
||||
}
|
||||
|
||||
size_t n = GlobalsToChange.size();
|
||||
@ -1223,28 +1239,26 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
|
||||
uint64_t MinRZ = MinRedzoneSizeForGlobal();
|
||||
// MinRZ <= RZ <= kMaxGlobalRedzone
|
||||
// and trying to make RZ to be ~ 1/4 of SizeInBytes.
|
||||
uint64_t RZ = std::max(MinRZ,
|
||||
std::min(kMaxGlobalRedzone,
|
||||
(SizeInBytes / MinRZ / 4) * MinRZ));
|
||||
uint64_t RZ = std::max(
|
||||
MinRZ, std::min(kMaxGlobalRedzone, (SizeInBytes / MinRZ / 4) * MinRZ));
|
||||
uint64_t RightRedzoneSize = RZ;
|
||||
// Round up to MinRZ
|
||||
if (SizeInBytes % MinRZ)
|
||||
RightRedzoneSize += MinRZ - (SizeInBytes % MinRZ);
|
||||
if (SizeInBytes % MinRZ) RightRedzoneSize += MinRZ - (SizeInBytes % MinRZ);
|
||||
assert(((RightRedzoneSize + SizeInBytes) % MinRZ) == 0);
|
||||
Type *RightRedZoneTy = ArrayType::get(IRB.getInt8Ty(), RightRedzoneSize);
|
||||
|
||||
StructType *NewTy = StructType::get(Ty, RightRedZoneTy, nullptr);
|
||||
Constant *NewInitializer = ConstantStruct::get(
|
||||
NewTy, G->getInitializer(),
|
||||
Constant *NewInitializer =
|
||||
ConstantStruct::get(NewTy, G->getInitializer(),
|
||||
Constant::getNullValue(RightRedZoneTy), nullptr);
|
||||
|
||||
// Create a new global variable with enough space for a redzone.
|
||||
GlobalValue::LinkageTypes Linkage = G->getLinkage();
|
||||
if (G->isConstant() && Linkage == GlobalValue::PrivateLinkage)
|
||||
Linkage = GlobalValue::InternalLinkage;
|
||||
GlobalVariable *NewGlobal = new GlobalVariable(
|
||||
M, NewTy, G->isConstant(), Linkage,
|
||||
NewInitializer, "", G, G->getThreadLocalMode());
|
||||
GlobalVariable *NewGlobal =
|
||||
new GlobalVariable(M, NewTy, G->isConstant(), Linkage, NewInitializer,
|
||||
"", G, G->getThreadLocalMode());
|
||||
NewGlobal->copyAttributesFrom(G);
|
||||
NewGlobal->setAlignment(MinRZ);
|
||||
|
||||
@ -1273,8 +1287,7 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
|
||||
ConstantExpr::getPointerCast(ModuleName, IntptrTy),
|
||||
ConstantInt::get(IntptrTy, MD.IsDynInit), SourceLoc, nullptr);
|
||||
|
||||
if (ClInitializers && MD.IsDynInit)
|
||||
HasDynamicallyInitializedGlobals = true;
|
||||
if (ClInitializers && MD.IsDynInit) HasDynamicallyInitializedGlobals = true;
|
||||
|
||||
DEBUG(dbgs() << "NEW GLOBAL: " << *NewGlobal << "\n");
|
||||
}
|
||||
@ -1293,8 +1306,8 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
|
||||
|
||||
// We also need to unregister globals at the end, e.g. when a shared library
|
||||
// gets closed.
|
||||
Function *AsanDtorFunction = Function::Create(
|
||||
FunctionType::get(Type::getVoidTy(*C), false),
|
||||
Function *AsanDtorFunction =
|
||||
Function::Create(FunctionType::get(Type::getVoidTy(*C), false),
|
||||
GlobalValue::InternalLinkage, kAsanModuleDtorName, &M);
|
||||
BasicBlock *AsanDtorBB = BasicBlock::Create(*C, "", AsanDtorFunction);
|
||||
IRBuilder<> IRB_Dtor(ReturnInst::Create(*C, AsanDtorBB));
|
||||
@ -1309,8 +1322,7 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
|
||||
|
||||
bool AddressSanitizerModule::runOnModule(Module &M) {
|
||||
DataLayoutPass *DLP = getAnalysisIfAvailable<DataLayoutPass>();
|
||||
if (!DLP)
|
||||
return false;
|
||||
if (!DLP) return false;
|
||||
DL = &DLP->getDataLayout();
|
||||
C = &(M.getContext());
|
||||
int LongSize = DL->getPointerSizeInBits();
|
||||
@ -1325,8 +1337,7 @@ bool AddressSanitizerModule::runOnModule(Module &M) {
|
||||
assert(CtorFunc);
|
||||
IRBuilder<> IRB(CtorFunc->getEntryBlock().getTerminator());
|
||||
|
||||
if (ClGlobals)
|
||||
Changed |= InstrumentGlobals(IRB, M);
|
||||
if (ClGlobals) Changed |= InstrumentGlobals(IRB, M);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
@ -1389,8 +1400,7 @@ void AddressSanitizer::initializeCallbacks(Module &M) {
|
||||
bool AddressSanitizer::doInitialization(Module &M) {
|
||||
// Initialize the private fields. No one has accessed them before.
|
||||
DataLayoutPass *DLP = getAnalysisIfAvailable<DataLayoutPass>();
|
||||
if (!DLP)
|
||||
report_fatal_error("data layout missing");
|
||||
if (!DLP) report_fatal_error("data layout missing");
|
||||
DL = &DLP->getDataLayout();
|
||||
|
||||
GlobalsMD.init(M);
|
||||
@ -1400,8 +1410,8 @@ bool AddressSanitizer::doInitialization(Module &M) {
|
||||
IntptrTy = Type::getIntNTy(*C, LongSize);
|
||||
TargetTriple = Triple(M.getTargetTriple());
|
||||
|
||||
AsanCtorFunction = Function::Create(
|
||||
FunctionType::get(Type::getVoidTy(*C), false),
|
||||
AsanCtorFunction =
|
||||
Function::Create(FunctionType::get(Type::getVoidTy(*C), false),
|
||||
GlobalValue::InternalLinkage, kAsanModuleCtorName, &M);
|
||||
BasicBlock *AsanCtorBB = BasicBlock::Create(*C, "", AsanCtorFunction);
|
||||
// call __asan_init in the module ctor.
|
||||
@ -1444,11 +1454,9 @@ bool AddressSanitizer::runOnFunction(Function &F) {
|
||||
// If needed, insert __asan_init before checking for SanitizeAddress attr.
|
||||
maybeInsertAsanInitAtFunctionEntry(F);
|
||||
|
||||
if (!F.hasFnAttribute(Attribute::SanitizeAddress))
|
||||
return false;
|
||||
if (!F.hasFnAttribute(Attribute::SanitizeAddress)) return false;
|
||||
|
||||
if (!ClDebugFunc.empty() && ClDebugFunc != F.getName())
|
||||
return false;
|
||||
if (!ClDebugFunc.empty() && ClDebugFunc != F.getName()) return false;
|
||||
|
||||
// We want to instrument every address only once per basic block (unless there
|
||||
// are calls between uses).
|
||||
@ -1460,6 +1468,7 @@ bool AddressSanitizer::runOnFunction(Function &F) {
|
||||
int NumAllocas = 0;
|
||||
bool IsWrite;
|
||||
unsigned Alignment;
|
||||
uint64_t TypeSize;
|
||||
|
||||
// Fill the set of memory operations to instrument.
|
||||
for (auto &BB : F) {
|
||||
@ -1468,8 +1477,8 @@ bool AddressSanitizer::runOnFunction(Function &F) {
|
||||
int NumInsnsPerBB = 0;
|
||||
for (auto &Inst : BB) {
|
||||
if (LooksLikeCodeInBug11395(&Inst)) return false;
|
||||
if (Value *Addr =
|
||||
isInterestingMemoryAccess(&Inst, &IsWrite, &Alignment)) {
|
||||
if (Value *Addr = isInterestingMemoryAccess(&Inst, &IsWrite, &TypeSize,
|
||||
&Alignment)) {
|
||||
if (ClOpt && ClOptSameTemp) {
|
||||
if (!TempsToInstrument.insert(Addr).second)
|
||||
continue; // We've seen this temp in the current BB.
|
||||
@ -1481,21 +1490,18 @@ bool AddressSanitizer::runOnFunction(Function &F) {
|
||||
} else if (isa<MemIntrinsic>(Inst)) {
|
||||
// ok, take it.
|
||||
} else {
|
||||
if (isa<AllocaInst>(Inst))
|
||||
NumAllocas++;
|
||||
if (isa<AllocaInst>(Inst)) NumAllocas++;
|
||||
CallSite CS(&Inst);
|
||||
if (CS) {
|
||||
// A call inside BB.
|
||||
TempsToInstrument.clear();
|
||||
if (CS.doesNotReturn())
|
||||
NoReturnCalls.push_back(CS.getInstruction());
|
||||
if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ToInstrument.push_back(&Inst);
|
||||
NumInsnsPerBB++;
|
||||
if (NumInsnsPerBB >= ClMaxInsnsToInstrumentPerBB)
|
||||
break;
|
||||
if (NumInsnsPerBB >= ClMaxInsnsToInstrumentPerBB) break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1504,13 +1510,18 @@ bool AddressSanitizer::runOnFunction(Function &F) {
|
||||
ToInstrument.size() > (unsigned)ClInstrumentationWithCallsThreshold)
|
||||
UseCalls = true;
|
||||
|
||||
const TargetLibraryInfo *TLI =
|
||||
&getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
|
||||
ObjectSizeOffsetVisitor ObjSizeVis(DL, TLI, F.getContext(),
|
||||
/*RoundToAlign=*/true);
|
||||
|
||||
// Instrument.
|
||||
int NumInstrumented = 0;
|
||||
for (auto Inst : ToInstrument) {
|
||||
if (ClDebugMin < 0 || ClDebugMax < 0 ||
|
||||
(NumInstrumented >= ClDebugMin && NumInstrumented <= ClDebugMax)) {
|
||||
if (isInterestingMemoryAccess(Inst, &IsWrite, &Alignment))
|
||||
instrumentMop(Inst, UseCalls);
|
||||
if (isInterestingMemoryAccess(Inst, &IsWrite, &TypeSize, &Alignment))
|
||||
instrumentMop(ObjSizeVis, Inst, UseCalls);
|
||||
else
|
||||
instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
|
||||
}
|
||||
@ -1569,8 +1580,7 @@ void FunctionStackPoisoner::initializeCallbacks(Module &M) {
|
||||
IntptrTy, IntptrTy, nullptr));
|
||||
}
|
||||
|
||||
void
|
||||
FunctionStackPoisoner::poisonRedZones(ArrayRef<uint8_t> ShadowBytes,
|
||||
void FunctionStackPoisoner::poisonRedZones(ArrayRef<uint8_t> ShadowBytes,
|
||||
IRBuilder<> &IRB, Value *ShadowBase,
|
||||
bool DoPoison) {
|
||||
size_t n = ShadowBytes.size();
|
||||
@ -1603,8 +1613,7 @@ static int StackMallocSizeClass(uint64_t LocalStackSize) {
|
||||
assert(LocalStackSize <= kMaxStackMallocSize);
|
||||
uint64_t MaxSize = kMinStackMallocSize;
|
||||
for (int i = 0;; i++, MaxSize *= 2)
|
||||
if (LocalStackSize <= MaxSize)
|
||||
return i;
|
||||
if (LocalStackSize <= MaxSize) return i;
|
||||
llvm_unreachable("impossible LocalStackSize");
|
||||
}
|
||||
|
||||
@ -1626,8 +1635,7 @@ void FunctionStackPoisoner::SetShadowToStackAfterReturnInlined(
|
||||
|
||||
static DebugLoc getFunctionEntryDebugLocation(Function &F) {
|
||||
for (const auto &Inst : F.getEntryBlock())
|
||||
if (!isa<AllocaInst>(Inst))
|
||||
return Inst.getDebugLoc();
|
||||
if (!isa<AllocaInst>(Inst)) return Inst.getDebugLoc();
|
||||
return DebugLoc();
|
||||
}
|
||||
|
||||
@ -1777,18 +1785,18 @@ void FunctionStackPoisoner::poisonStack() {
|
||||
BasePlus0);
|
||||
// Write the frame description constant to redzone[1].
|
||||
Value *BasePlus1 = IRB.CreateIntToPtr(
|
||||
IRB.CreateAdd(LocalStackBase, ConstantInt::get(IntptrTy, ASan.LongSize/8)),
|
||||
IRB.CreateAdd(LocalStackBase,
|
||||
ConstantInt::get(IntptrTy, ASan.LongSize / 8)),
|
||||
IntptrPtrTy);
|
||||
GlobalVariable *StackDescriptionGlobal =
|
||||
createPrivateGlobalForString(*F.getParent(), L.DescriptionString,
|
||||
/*AllowMerging*/ true);
|
||||
Value *Description = IRB.CreatePointerCast(StackDescriptionGlobal,
|
||||
IntptrTy);
|
||||
Value *Description = IRB.CreatePointerCast(StackDescriptionGlobal, IntptrTy);
|
||||
IRB.CreateStore(Description, BasePlus1);
|
||||
// Write the PC to redzone[2].
|
||||
Value *BasePlus2 = IRB.CreateIntToPtr(
|
||||
IRB.CreateAdd(LocalStackBase, ConstantInt::get(IntptrTy,
|
||||
2 * ASan.LongSize/8)),
|
||||
IRB.CreateAdd(LocalStackBase,
|
||||
ConstantInt::get(IntptrTy, 2 * ASan.LongSize / 8)),
|
||||
IntptrPtrTy);
|
||||
IRB.CreateStore(IRB.CreatePointerCast(&F, IntptrTy), BasePlus2);
|
||||
|
||||
@ -1850,8 +1858,7 @@ void FunctionStackPoisoner::poisonStack() {
|
||||
}
|
||||
|
||||
// We are done. Remove the old unused alloca instructions.
|
||||
for (auto AI : AllocaVec)
|
||||
AI->eraseFromParent();
|
||||
for (auto AI : AllocaVec) AI->eraseFromParent();
|
||||
}
|
||||
|
||||
void FunctionStackPoisoner::poisonAlloca(Value *V, uint64_t Size,
|
||||
@ -1859,8 +1866,8 @@ void FunctionStackPoisoner::poisonAlloca(Value *V, uint64_t Size,
|
||||
// For now just insert the call to ASan runtime.
|
||||
Value *AddrArg = IRB.CreatePointerCast(V, IntptrTy);
|
||||
Value *SizeArg = ConstantInt::get(IntptrTy, Size);
|
||||
IRB.CreateCall2(DoPoison ? AsanPoisonStackMemoryFunc
|
||||
: AsanUnpoisonStackMemoryFunc,
|
||||
IRB.CreateCall2(
|
||||
DoPoison ? AsanPoisonStackMemoryFunc : AsanUnpoisonStackMemoryFunc,
|
||||
AddrArg, SizeArg);
|
||||
}
|
||||
|
||||
@ -1880,8 +1887,7 @@ AllocaInst *FunctionStackPoisoner::findAllocaForValue(Value *V) {
|
||||
// See if we've already calculated (or started to calculate) alloca for a
|
||||
// given value.
|
||||
AllocaForValueMapTy::iterator I = AllocaForValue.find(V);
|
||||
if (I != AllocaForValue.end())
|
||||
return I->second;
|
||||
if (I != AllocaForValue.end()) return I->second;
|
||||
// Store 0 while we're calculating alloca for value V to avoid
|
||||
// infinite recursion if the value references itself.
|
||||
AllocaForValue[V] = nullptr;
|
||||
@ -1900,8 +1906,7 @@ AllocaInst *FunctionStackPoisoner::findAllocaForValue(Value *V) {
|
||||
Res = IncValueAI;
|
||||
}
|
||||
}
|
||||
if (Res)
|
||||
AllocaForValue[V] = Res;
|
||||
if (Res) AllocaForValue[V] = Res;
|
||||
return Res;
|
||||
}
|
||||
|
||||
@ -2041,3 +2046,20 @@ void FunctionStackPoisoner::handleDynamicAllocaCall(
|
||||
AI->eraseFromParent();
|
||||
NumInstrumentedDynamicAllocas++;
|
||||
}
|
||||
|
||||
// isSafeAccess returns true if Addr is always inbounds with respect to its
|
||||
// base object. For example, it is a field access or an array access with
|
||||
// constant inbounds index.
|
||||
bool AddressSanitizer::isSafeAccess(ObjectSizeOffsetVisitor &ObjSizeVis,
|
||||
Value *Addr, uint64_t TypeSize) const {
|
||||
SizeOffsetType SizeOffset = ObjSizeVis.compute(Addr);
|
||||
if (!ObjSizeVis.bothKnown(SizeOffset)) return false;
|
||||
int64_t Size = SizeOffset.first.getSExtValue();
|
||||
int64_t Offset = SizeOffset.second.getSExtValue();
|
||||
// Three checks are required to ensure safety:
|
||||
// . Offset >= 0 (since the offset is given from the base ptr)
|
||||
// . Size >= Offset (unsigned)
|
||||
// . Size - Offset >= NeededSize (unsigned)
|
||||
return Offset >= 0 && Size >= Offset &&
|
||||
uint64_t(Size - Offset) >= TypeSize / 8;
|
||||
}
|
||||
|
48
test/Instrumentation/AddressSanitizer/instrument-stack.ll
Normal file
48
test/Instrumentation/AddressSanitizer/instrument-stack.ll
Normal file
@ -0,0 +1,48 @@
|
||||
; This test checks that we are not instrumenting direct inbound stack accesses.
|
||||
; RUN: opt < %s -asan -asan-module -asan-opt-stack -S | FileCheck %s
|
||||
|
||||
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-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
;@sink = global i32* null, align 4
|
||||
|
||||
; Ignore direct inbounds stack access.
|
||||
define void @foo() uwtable sanitize_address {
|
||||
entry:
|
||||
%a = alloca i32, align 4
|
||||
store i32 42, i32* %a, align 4
|
||||
ret void
|
||||
; CHECK-LABEL: define void @foo
|
||||
; CHECK-NOT: __asan_report
|
||||
; CHECK: ret void
|
||||
}
|
||||
|
||||
; Don't ignore dynamic indexing.
|
||||
define void @baz(i64 %i) sanitize_address {
|
||||
entry:
|
||||
%a = alloca [10 x i32], align 4
|
||||
%e = getelementptr inbounds [10 x i32], [10 x i32]* %a, i32 0, i64 %i
|
||||
store i32 42, i32* %e, align 4
|
||||
ret void
|
||||
; CHECK-LABEL: define void @baz
|
||||
; CHECK: __asan_report
|
||||
; CHECK: ret void
|
||||
}
|
||||
|
||||
define void @bar() sanitize_address {
|
||||
entry:
|
||||
%a = alloca [10 x i32], align 4
|
||||
%e = getelementptr inbounds [10 x i32], [10 x i32]* %a, i32 0, i64 12
|
||||
store i32 42, i32* %e, align 4
|
||||
ret void
|
||||
; CHECK-LABEL: define void @bar
|
||||
; CHECK: __asan_report
|
||||
; CHECK: ret void
|
||||
}
|
||||
|
||||
define void @endoftests() sanitize_address {
|
||||
entry:
|
||||
ret void
|
||||
; CHECK-LABEL: define void @endoftests
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user