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