Index: include/llvm/IR/AddressSanitizerPass.h =================================================================== --- /dev/null +++ include/llvm/IR/AddressSanitizerPass.h @@ -0,0 +1,259 @@ +#ifndef LLVM_IR_ADDRESSSANITIZERNEWPASS_H +#define LLVM_IR_ADDRESSSANITIZERNEWPASS_H + +#include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Value.h" + +namespace llvm { + +// Accesses sizes are powers of two: 1, 2, 4, 8, 16. +static const size_t kNumberOfAccessSizes = 5; + +/// This struct defines the shadow mapping using the rule: +/// shadow = (mem >> Scale) ADD-or-OR Offset. +/// If InGlobal is true, then +/// extern char __asan_shadow[]; +/// shadow = (mem >> Scale) + &__asan_shadow +struct ShadowMapping { + int Scale; + uint64_t Offset; + bool OrShadowOffset; + bool InGlobal; +}; + +/// Frontend-provided metadata for source location. +struct LocationMetadata { + StringRef Filename; + int LineNo = 0; + int ColumnNo = 0; + + LocationMetadata() = default; + + bool empty() const { return Filename.empty(); } + + void parse(MDNode *MDN) { + assert(MDN->getNumOperands() == 3); + MDString *DIFilename = cast(MDN->getOperand(0)); + Filename = DIFilename->getString(); + LineNo = + mdconst::extract(MDN->getOperand(1))->getLimitedValue(); + ColumnNo = + mdconst::extract(MDN->getOperand(2))->getLimitedValue(); + } +}; + +/// Frontend-provided metadata for global variables. +class GlobalsMetadata { +public: + struct Entry { + LocationMetadata SourceLoc; + StringRef Name; + bool IsDynInit = false; + bool IsBlacklisted = false; + + Entry() = default; + }; + + GlobalsMetadata() = default; + + void reset() { + inited_ = false; + Entries.clear(); + } + + void init(Module &M); + + /// Returns metadata entry for a given global. + Entry get(GlobalVariable *G) const { + auto Pos = Entries.find(G); + return (Pos != Entries.end()) ? Pos->second : Entry(); + } + +private: + bool inited_ = false; + DenseMap Entries; +}; + +struct FunctionStackPoisoner; + +class AddressSanitizer { +public: + explicit AddressSanitizer(bool CompileKernel = false, bool Recover = false, + bool UseAfterScope = false); + + /// Return true if the function changed. + bool instrument(Function &F, DominatorTree *DTree, + const TargetLibraryInfo *TLI); + PreservedAnalyses run(Function &F, AnalysisManager &AM); + + static StringRef name() { return "AddressSanitizerFunctionPass"; } + + bool maybeInsertAsanInitAtFunctionEntry(Function &F); + void initializeCallbacks(Module &M); + void maybeInsertDynamicShadowAtFunctionEntry(Function &F); + void markEscapedLocalAllocas(Function &F); + bool isInterestingAlloca(const AllocaInst &AI); + void instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis, Instruction *I, + bool UseCalls, const DataLayout &DL); + + /// If it is an interesting memory access, return the PointerOperand + /// and set IsWrite/Alignment. Otherwise return nullptr. + /// MaybeMask is an output parameter for the mask Value, if we're looking at a + /// masked load/store. + Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite, + uint64_t *TypeSize, unsigned *Alignment, + Value **MaybeMask = nullptr); + void instrumentAddress(Instruction *OrigIns, Instruction *InsertBefore, + Value *Addr, uint32_t TypeSize, bool IsWrite, + Value *SizeArgument, bool UseCalls, uint32_t Exp); + void instrumentUnusualSizeOrAlignment(Instruction *I, + Instruction *InsertBefore, Value *Addr, + uint32_t TypeSize, bool IsWrite, + Value *SizeArgument, bool UseCalls, + uint32_t Exp); + Value *memToShadow(Value *Shadow, IRBuilder<> &IRB); + Value *createSlowPathCmp(IRBuilder<> &IRB, Value *AddrLong, + Value *ShadowValue, uint32_t TypeSize); + Instruction *generateCrashCode(Instruction *InsertBefore, Value *Addr, + bool IsWrite, size_t AccessSizeIndex, + Value *SizeArgument, uint32_t Exp); + void instrumentMemIntrinsic(MemIntrinsic *MI); + void instrumentPointerComparisonOrSubtraction(Instruction *I); + + uint64_t getAllocaSizeInBytes(const AllocaInst &AI) const { + uint64_t ArraySize = 1; + if (AI.isArrayAllocation()) { + const ConstantInt *CI = dyn_cast(AI.getArraySize()); + assert(CI && "non-constant array size"); + ArraySize = CI->getZExtValue(); + } + Type *Ty = AI.getAllocatedType(); + uint64_t SizeInBytes = AI.getModule()->getDataLayout().getTypeAllocSize(Ty); + return SizeInBytes * ArraySize; + } + + DominatorTree &getDominatorTree() const { return *DT; } + +private: + friend struct FunctionStackPoisoner; + + /// Helper to cleanup per-function state. + struct FunctionStateRAII { + AddressSanitizer *Pass; + + FunctionStateRAII(AddressSanitizer *Pass) : Pass(Pass) { + assert(Pass->ProcessedAllocas.empty() && + "last pass forgot to clear cache"); + assert(!Pass->LocalDynamicShadow); + } + + ~FunctionStateRAII() { + Pass->LocalDynamicShadow = nullptr; + Pass->ProcessedAllocas.clear(); + } + }; + + bool LooksLikeCodeInBug11395(Instruction *I); + bool GlobalIsLinkerInitialized(GlobalVariable *G); + bool isSafeAccess(ObjectSizeOffsetVisitor &ObjSizeVis, Value *Addr, + uint64_t TypeSize) const; + + bool Inited = false; + LLVMContext *C; + Triple TargetTriple; + int LongSize; + bool CompileKernel; + bool Recover; + bool UseAfterScope; + Type *IntptrTy; + ShadowMapping Mapping; + DominatorTree *DT; + Function *AsanHandleNoReturnFunc; + Function *AsanPtrCmpFunction, *AsanPtrSubFunction; + Constant *AsanShadowGlobal; + + // These arrays is indexed by AccessIsWrite, Experiment and log2(AccessSize). + Function *AsanErrorCallback[2][2][kNumberOfAccessSizes]; + Function *AsanMemoryAccessCallback[2][2][kNumberOfAccessSizes]; + + // These arrays is indexed by AccessIsWrite and Experiment. + Function *AsanErrorCallbackSized[2][2]; + Function *AsanMemoryAccessCallbackSized[2][2]; + + Function *AsanMemmove, *AsanMemcpy, *AsanMemset; + InlineAsm *EmptyAsm; + Value *LocalDynamicShadow = nullptr; + GlobalsMetadata GlobalsMD; + DenseMap ProcessedAllocas; +}; + +class AddressSanitizerModule { +public: + explicit AddressSanitizerModule(bool CompileKernel = false, + bool Recover = false, + bool UseGlobalsGC = true); + + PreservedAnalyses run(Module &M, AnalysisManager &AM); + + /// Returns true if the module was modified. + bool instrument(Module &M); + + static StringRef name() { return "AddressSanitizerModule"; } + +private: + void initializeCallbacks(Module &M); + int GetAsanVersion(const Module &M) const; + bool InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool *CtorComdat); + bool ShouldInstrumentGlobal(GlobalVariable *G); + size_t MinRedzoneSizeForGlobal() const; + void InstrumentGlobalsELF(IRBuilder<> &IRB, Module &M, + ArrayRef ExtendedGlobals, + ArrayRef MetadataInitializers, + const std::string &UniqueModuleId); + void InstrumentGlobalsCOFF(IRBuilder<> &IRB, Module &M, + ArrayRef ExtendedGlobals, + ArrayRef MetadataInitializers); + bool ShouldUseMachOGlobalsSection() const; + void InstrumentGlobalsMachO(IRBuilder<> &IRB, Module &M, + ArrayRef ExtendedGlobals, + ArrayRef MetadataInitializers); + void + InstrumentGlobalsWithMetadataArray(IRBuilder<> &IRB, Module &M, + ArrayRef ExtendedGlobals, + ArrayRef MetadataInitializers); + void createInitializerPoisonCalls(Module &M, GlobalValue *ModuleName); + void poisonOneInitializer(Function &GlobalInit, GlobalValue *ModuleName); + GlobalVariable *CreateMetadataGlobal(Module &M, Constant *Initializer, + StringRef OriginalName); + void SetComdatForGlobalMetadata(GlobalVariable *G, GlobalVariable *Metadata, + StringRef InternalSuffix); + StringRef getGlobalMetadataSection() const; + IRBuilder<> CreateAsanModuleDtor(Module &M); + + GlobalsMetadata GlobalsMD; + bool CompileKernel; + bool Recover; + bool UseGlobalsGC; + bool UseCtorComdat; + Type *IntptrTy; + LLVMContext *C; + Triple TargetTriple; + ShadowMapping Mapping; + Function *AsanPoisonGlobals; + Function *AsanUnpoisonGlobals; + Function *AsanRegisterGlobals; + Function *AsanUnregisterGlobals; + Function *AsanRegisterImageGlobals; + Function *AsanUnregisterImageGlobals; + Function *AsanRegisterElfGlobals; + Function *AsanUnregisterElfGlobals; + + Function *AsanCtorFunction = nullptr; + Function *AsanDtorFunction = nullptr; +}; + +} // namespace llvm + +#endif Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -65,8 +65,8 @@ void initializeAAResultsWrapperPassPass(PassRegistry&); void initializeADCELegacyPassPass(PassRegistry&); void initializeAddDiscriminatorsLegacyPassPass(PassRegistry&); -void initializeAddressSanitizerModulePass(PassRegistry&); -void initializeAddressSanitizerPass(PassRegistry&); +void initializeAddressSanitizerModuleLegacyPass(PassRegistry &); +void initializeAddressSanitizerLegacyPass(PassRegistry &); void initializeAggressiveInstCombinerLegacyPassPass(PassRegistry&); void initializeAliasSetPrinterPass(PassRegistry&); void initializeAlignmentFromAssumptionsPass(PassRegistry&); Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -53,6 +53,7 @@ #include "llvm/Analysis/TypeBasedAliasAnalysis.h" #include "llvm/CodeGen/PreISelIntrinsicLowering.h" #include "llvm/CodeGen/UnreachableBlockElim.h" +#include "llvm/IR/AddressSanitizerPass.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/PassManager.h" @@ -61,7 +62,6 @@ #include "llvm/Support/Regex.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h" -#include "llvm/Transforms/Instrumentation/CGProfile.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/ArgumentPromotion.h" #include "llvm/Transforms/IPO/CalledValuePropagation.h" @@ -87,6 +87,7 @@ #include "llvm/Transforms/IPO/WholeProgramDevirt.h" #include "llvm/Transforms/InstCombine/InstCombine.h" #include "llvm/Transforms/Instrumentation/BoundsChecking.h" +#include "llvm/Transforms/Instrumentation/CGProfile.h" #include "llvm/Transforms/Instrumentation/ControlHeightReduction.h" #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h" Index: lib/Passes/PassRegistry.def =================================================================== --- lib/Passes/PassRegistry.def +++ lib/Passes/PassRegistry.def @@ -40,6 +40,7 @@ #define MODULE_PASS(NAME, CREATE_PASS) #endif MODULE_PASS("always-inline", AlwaysInlinerPass()) +MODULE_PASS("asan-module", AddressSanitizerModule()) MODULE_PASS("called-value-propagation", CalledValuePropagationPass()) MODULE_PASS("cg-profile", CGProfilePass()) MODULE_PASS("constmerge", ConstantMergePass()) @@ -146,6 +147,7 @@ FUNCTION_PASS("add-discriminators", AddDiscriminatorsPass()) FUNCTION_PASS("aggressive-instcombine", AggressiveInstCombinePass()) FUNCTION_PASS("alignment-from-assumptions", AlignmentFromAssumptionsPass()) +FUNCTION_PASS("asan", AddressSanitizer()) FUNCTION_PASS("bdce", BDCEPass()) FUNCTION_PASS("bounds-checking", BoundsCheckingPass()) FUNCTION_PASS("break-crit-edges", BreakCriticalEdgesPass()) Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -25,9 +25,9 @@ #include "llvm/ADT/Twine.h" #include "llvm/Analysis/MemoryBuiltins.h" #include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/Transforms/Utils/Local.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/IR/AddressSanitizerPass.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" @@ -72,6 +72,7 @@ #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Utils/ASanStackFrameLayout.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" #include @@ -176,9 +177,6 @@ static const char *const kAsanAllocaPoison = "__asan_alloca_poison"; static const char *const kAsanAllocasUnpoison = "__asan_allocas_unpoison"; -// Accesses sizes are powers of two: 1, 2, 4, 8, 16. -static const size_t kNumberOfAccessSizes = 5; - static const unsigned kAllocaRzSize = 32; // Command-line flags. @@ -388,96 +386,6 @@ namespace { -/// Frontend-provided metadata for source location. -struct LocationMetadata { - StringRef Filename; - int LineNo = 0; - int ColumnNo = 0; - - LocationMetadata() = default; - - bool empty() const { return Filename.empty(); } - - void parse(MDNode *MDN) { - assert(MDN->getNumOperands() == 3); - MDString *DIFilename = cast(MDN->getOperand(0)); - Filename = DIFilename->getString(); - LineNo = - mdconst::extract(MDN->getOperand(1))->getLimitedValue(); - ColumnNo = - mdconst::extract(MDN->getOperand(2))->getLimitedValue(); - } -}; - -/// Frontend-provided metadata for global variables. -class GlobalsMetadata { -public: - struct Entry { - LocationMetadata SourceLoc; - StringRef Name; - bool IsDynInit = false; - bool IsBlacklisted = false; - - Entry() = default; - }; - - GlobalsMetadata() = default; - - void reset() { - inited_ = false; - Entries.clear(); - } - - void init(Module &M) { - assert(!inited_); - inited_ = true; - NamedMDNode *Globals = M.getNamedMetadata("llvm.asan.globals"); - 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(MDN->getOperand(0)); - // The optimizer may optimize away a global entirely. - if (!GV) continue; - // We can already have an entry for GV if it was merged with another - // global. - Entry &E = Entries[GV]; - if (auto *Loc = cast_or_null(MDN->getOperand(1))) - E.SourceLoc.parse(Loc); - if (auto *Name = cast_or_null(MDN->getOperand(2))) - E.Name = Name->getString(); - ConstantInt *IsDynInit = - mdconst::extract(MDN->getOperand(3)); - E.IsDynInit |= IsDynInit->isOne(); - ConstantInt *IsBlacklisted = - mdconst::extract(MDN->getOperand(4)); - E.IsBlacklisted |= IsBlacklisted->isOne(); - } - } - - /// Returns metadata entry for a given global. - Entry get(GlobalVariable *G) const { - auto Pos = Entries.find(G); - return (Pos != Entries.end()) ? Pos->second : Entry(); - } - -private: - bool inited_ = false; - DenseMap Entries; -}; - -/// This struct defines the shadow mapping using the rule: -/// shadow = (mem >> Scale) ADD-or-OR Offset. -/// If InGlobal is true, then -/// extern char __asan_shadow[]; -/// shadow = (mem >> Scale) + &__asan_shadow -struct ShadowMapping { - int Scale; - uint64_t Offset; - bool OrShadowOffset; - bool InGlobal; -}; - } // end anonymous namespace static ShadowMapping getShadowMapping(Triple &TargetTriple, int LongSize, @@ -594,220 +502,191 @@ return std::max(32U, 1U << MappingScale); } +size_t AddressSanitizerModule::MinRedzoneSizeForGlobal() const { + return RedzoneSizeForScale(Mapping.Scale); +} + +bool AddressSanitizer::maybeInsertAsanInitAtFunctionEntry(Function &F) { + // For each NSObject descendant having a +load method, this method is invoked + // by the ObjC runtime before any of the static constructors is called. + // Therefore we need to instrument such methods with a call to __asan_init + // at the beginning in order to initialize our runtime before any access to + // the shadow memory. + // We cannot just ignore these methods, because they may call other + // instrumented functions. + if (F.getName().find(" load]") != std::string::npos) { + Function *AsanInitFunction = + declareSanitizerInitFunction(*F.getParent(), kAsanInitName, {}); + IRBuilder<> IRB(&F.front(), F.front().begin()); + IRB.CreateCall(AsanInitFunction, {}); + return true; + } + return false; +} + +void AddressSanitizer::initializeCallbacks(Module &M) { + IRBuilder<> IRB(*C); + // Create __asan_report* callbacks. + // IsWrite, TypeSize and Exp are encoded in the function name. + for (int Exp = 0; Exp < 2; Exp++) { + for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) { + const std::string TypeStr = AccessIsWrite ? "store" : "load"; + const std::string ExpStr = Exp ? "exp_" : ""; + const std::string EndingStr = Recover ? "_noabort" : ""; + + SmallVector Args2 = {IntptrTy, IntptrTy}; + SmallVector Args1{1, IntptrTy}; + if (Exp) { + Type *ExpType = Type::getInt32Ty(*C); + Args2.push_back(ExpType); + Args1.push_back(ExpType); + } + AsanErrorCallbackSized[AccessIsWrite][Exp] = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + kAsanReportErrorTemplate + ExpStr + TypeStr + "_n" + EndingStr, + FunctionType::get(IRB.getVoidTy(), Args2, false))); + + AsanMemoryAccessCallbackSized[AccessIsWrite][Exp] = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + ClMemoryAccessCallbackPrefix + ExpStr + TypeStr + "N" + EndingStr, + FunctionType::get(IRB.getVoidTy(), Args2, false))); + + for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes; + AccessSizeIndex++) { + const std::string Suffix = TypeStr + itostr(1ULL << AccessSizeIndex); + AsanErrorCallback[AccessIsWrite][Exp][AccessSizeIndex] = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + kAsanReportErrorTemplate + ExpStr + Suffix + EndingStr, + FunctionType::get(IRB.getVoidTy(), Args1, false))); + + AsanMemoryAccessCallback[AccessIsWrite][Exp][AccessSizeIndex] = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + ClMemoryAccessCallbackPrefix + ExpStr + Suffix + EndingStr, + FunctionType::get(IRB.getVoidTy(), Args1, false))); + } + } + } + + const std::string MemIntrinCallbackPrefix = + CompileKernel ? std::string("") : ClMemoryAccessCallbackPrefix; + AsanMemmove = checkSanitizerInterfaceFunction(M.getOrInsertFunction( + MemIntrinCallbackPrefix + "memmove", IRB.getInt8PtrTy(), + IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy)); + AsanMemcpy = checkSanitizerInterfaceFunction(M.getOrInsertFunction( + MemIntrinCallbackPrefix + "memcpy", IRB.getInt8PtrTy(), + IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy)); + AsanMemset = checkSanitizerInterfaceFunction(M.getOrInsertFunction( + MemIntrinCallbackPrefix + "memset", IRB.getInt8PtrTy(), + IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy)); + + AsanHandleNoReturnFunc = checkSanitizerInterfaceFunction( + M.getOrInsertFunction(kAsanHandleNoReturnName, IRB.getVoidTy())); + + AsanPtrCmpFunction = checkSanitizerInterfaceFunction( + M.getOrInsertFunction(kAsanPtrCmp, IRB.getVoidTy(), IntptrTy, IntptrTy)); + AsanPtrSubFunction = checkSanitizerInterfaceFunction( + M.getOrInsertFunction(kAsanPtrSub, IRB.getVoidTy(), IntptrTy, IntptrTy)); + // We insert an empty inline asm after __asan_report* to avoid callback merge. + EmptyAsm = InlineAsm::get(FunctionType::get(IRB.getVoidTy(), false), + StringRef(""), StringRef(""), + /*hasSideEffects=*/true); + if (Mapping.InGlobal) + AsanShadowGlobal = M.getOrInsertGlobal("__asan_shadow", + ArrayType::get(IRB.getInt8Ty(), 0)); +} + +AddressSanitizerModule::AddressSanitizerModule(bool CompileKernel, bool Recover, + bool UseGlobalsGC) + : UseGlobalsGC(UseGlobalsGC && ClUseGlobalsGC), + // Not a typo: ClWithComdat is almost completely pointless without + // ClUseGlobalsGC (because then it only works on modules without + // globals, which are rare); it is a prerequisite for ClUseGlobalsGC; + // and both suffer from gold PR19002 for which UseGlobalsGC constructor + // argument is designed as workaround. Therefore, disable both + // ClWithComdat and ClUseGlobalsGC unless the frontend says it's ok to + // do globals-gc. + UseCtorComdat(UseGlobalsGC && ClWithComdat) { + this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover; + this->CompileKernel = + ClEnableKasan.getNumOccurrences() > 0 ? ClEnableKasan : CompileKernel; +} + +void GlobalsMetadata::init(Module &M) { + assert(!inited_); + inited_ = true; + NamedMDNode *Globals = M.getNamedMetadata("llvm.asan.globals"); + 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(MDN->getOperand(0)); + // The optimizer may optimize away a global entirely. + if (!GV) + continue; + // We can already have an entry for GV if it was merged with another + // global. + Entry &E = Entries[GV]; + if (auto *Loc = cast_or_null(MDN->getOperand(1))) + E.SourceLoc.parse(Loc); + if (auto *Name = cast_or_null(MDN->getOperand(2))) + E.Name = Name->getString(); + ConstantInt *IsDynInit = mdconst::extract(MDN->getOperand(3)); + E.IsDynInit |= IsDynInit->isOne(); + ConstantInt *IsBlacklisted = + mdconst::extract(MDN->getOperand(4)); + E.IsBlacklisted |= IsBlacklisted->isOne(); + } +} + namespace { -/// AddressSanitizer: instrument the code in module to find memory bugs. -struct AddressSanitizer : public FunctionPass { +// Legacy AddressSanitizer pass. Uses AddressSanitizer compliant with new +// PassManager infrastructure. +struct AddressSanitizerLegacy : public FunctionPass { // Pass identification, replacement for typeid static char ID; - explicit AddressSanitizer(bool CompileKernel = false, bool Recover = false, - bool UseAfterScope = false) - : FunctionPass(ID), UseAfterScope(UseAfterScope || ClUseAfterScope) { - this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover; - this->CompileKernel = ClEnableKasan.getNumOccurrences() > 0 ? - ClEnableKasan : CompileKernel; - initializeAddressSanitizerPass(*PassRegistry::getPassRegistry()); - } + explicit AddressSanitizerLegacy(bool CompileKernel = false, + bool Recover = false, + bool UseAfterScope = false) + : FunctionPass(ID), Sanitizer(CompileKernel, Recover, UseAfterScope) {} - StringRef getPassName() const override { - return "AddressSanitizerFunctionPass"; - } + StringRef getPassName() const override { return Sanitizer.name(); } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); AU.addRequired(); } - uint64_t getAllocaSizeInBytes(const AllocaInst &AI) const { - uint64_t ArraySize = 1; - if (AI.isArrayAllocation()) { - const ConstantInt *CI = dyn_cast(AI.getArraySize()); - assert(CI && "non-constant array size"); - ArraySize = CI->getZExtValue(); - } - Type *Ty = AI.getAllocatedType(); - uint64_t SizeInBytes = - AI.getModule()->getDataLayout().getTypeAllocSize(Ty); - return SizeInBytes * ArraySize; - } - - /// Check if we want (and can) handle this alloca. - bool isInterestingAlloca(const AllocaInst &AI); - - /// If it is an interesting memory access, return the PointerOperand - /// and set IsWrite/Alignment. Otherwise return nullptr. - /// MaybeMask is an output parameter for the mask Value, if we're looking at a - /// masked load/store. - Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite, - uint64_t *TypeSize, unsigned *Alignment, - Value **MaybeMask = nullptr); - - void instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis, Instruction *I, - bool UseCalls, const DataLayout &DL); - void instrumentPointerComparisonOrSubtraction(Instruction *I); - void instrumentAddress(Instruction *OrigIns, Instruction *InsertBefore, - Value *Addr, uint32_t TypeSize, bool IsWrite, - Value *SizeArgument, bool UseCalls, uint32_t Exp); - void instrumentUnusualSizeOrAlignment(Instruction *I, - Instruction *InsertBefore, Value *Addr, - uint32_t TypeSize, bool IsWrite, - Value *SizeArgument, bool UseCalls, - uint32_t Exp); - Value *createSlowPathCmp(IRBuilder<> &IRB, Value *AddrLong, - Value *ShadowValue, uint32_t TypeSize); - Instruction *generateCrashCode(Instruction *InsertBefore, Value *Addr, - bool IsWrite, size_t AccessSizeIndex, - Value *SizeArgument, uint32_t Exp); - void instrumentMemIntrinsic(MemIntrinsic *MI); - Value *memToShadow(Value *Shadow, IRBuilder<> &IRB); bool runOnFunction(Function &F) override; - bool maybeInsertAsanInitAtFunctionEntry(Function &F); - void maybeInsertDynamicShadowAtFunctionEntry(Function &F); - void markEscapedLocalAllocas(Function &F); - bool doInitialization(Module &M) override; - bool doFinalization(Module &M) override; - - DominatorTree &getDominatorTree() const { return *DT; } private: - friend struct FunctionStackPoisoner; - - void initializeCallbacks(Module &M); - - bool LooksLikeCodeInBug11395(Instruction *I); - bool GlobalIsLinkerInitialized(GlobalVariable *G); - bool isSafeAccess(ObjectSizeOffsetVisitor &ObjSizeVis, Value *Addr, - uint64_t TypeSize) const; - - /// Helper to cleanup per-function state. - struct FunctionStateRAII { - AddressSanitizer *Pass; - - FunctionStateRAII(AddressSanitizer *Pass) : Pass(Pass) { - assert(Pass->ProcessedAllocas.empty() && - "last pass forgot to clear cache"); - assert(!Pass->LocalDynamicShadow); - } - - ~FunctionStateRAII() { - Pass->LocalDynamicShadow = nullptr; - Pass->ProcessedAllocas.clear(); - } - }; - - LLVMContext *C; - Triple TargetTriple; - int LongSize; - bool CompileKernel; - bool Recover; - bool UseAfterScope; - Type *IntptrTy; - ShadowMapping Mapping; - DominatorTree *DT; - Function *AsanHandleNoReturnFunc; - Function *AsanPtrCmpFunction, *AsanPtrSubFunction; - Constant *AsanShadowGlobal; - - // These arrays is indexed by AccessIsWrite, Experiment and log2(AccessSize). - Function *AsanErrorCallback[2][2][kNumberOfAccessSizes]; - Function *AsanMemoryAccessCallback[2][2][kNumberOfAccessSizes]; - - // These arrays is indexed by AccessIsWrite and Experiment. - Function *AsanErrorCallbackSized[2][2]; - Function *AsanMemoryAccessCallbackSized[2][2]; - - Function *AsanMemmove, *AsanMemcpy, *AsanMemset; - InlineAsm *EmptyAsm; - Value *LocalDynamicShadow = nullptr; - GlobalsMetadata GlobalsMD; - DenseMap ProcessedAllocas; + AddressSanitizer Sanitizer; }; -class AddressSanitizerModule : public ModulePass { +// Legacy AddressSanitizerModule pass. Uses AddressSanitizerModule compliant +// with new PassManager infrastructure. +class AddressSanitizerModuleLegacy : public ModulePass { public: // Pass identification, replacement for typeid static char ID; - explicit AddressSanitizerModule(bool CompileKernel = false, - bool Recover = false, - bool UseGlobalsGC = true) - : ModulePass(ID), - UseGlobalsGC(UseGlobalsGC && ClUseGlobalsGC), - // Not a typo: ClWithComdat is almost completely pointless without - // ClUseGlobalsGC (because then it only works on modules without - // globals, which are rare); it is a prerequisite for ClUseGlobalsGC; - // and both suffer from gold PR19002 for which UseGlobalsGC constructor - // argument is designed as workaround. Therefore, disable both - // ClWithComdat and ClUseGlobalsGC unless the frontend says it's ok to - // do globals-gc. - UseCtorComdat(UseGlobalsGC && ClWithComdat) { - this->Recover = ClRecover.getNumOccurrences() > 0 ? - ClRecover : Recover; - this->CompileKernel = ClEnableKasan.getNumOccurrences() > 0 ? - ClEnableKasan : CompileKernel; - } - - bool runOnModule(Module &M) override; - StringRef getPassName() const override { return "AddressSanitizerModule"; } + explicit AddressSanitizerModuleLegacy(bool CompileKernel = false, + bool Recover = false, + bool UseGlobalsGC = true) + : ModulePass(ID), Sanitizer(CompileKernel, Recover, UseGlobalsGC) {} -private: - void initializeCallbacks(Module &M); + bool runOnModule(Module &M) override { return Sanitizer.instrument(M); } - bool InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool *CtorComdat); - void InstrumentGlobalsCOFF(IRBuilder<> &IRB, Module &M, - ArrayRef ExtendedGlobals, - ArrayRef MetadataInitializers); - void InstrumentGlobalsELF(IRBuilder<> &IRB, Module &M, - ArrayRef ExtendedGlobals, - ArrayRef MetadataInitializers, - const std::string &UniqueModuleId); - void InstrumentGlobalsMachO(IRBuilder<> &IRB, Module &M, - ArrayRef ExtendedGlobals, - ArrayRef MetadataInitializers); - void - InstrumentGlobalsWithMetadataArray(IRBuilder<> &IRB, Module &M, - ArrayRef ExtendedGlobals, - ArrayRef MetadataInitializers); - - GlobalVariable *CreateMetadataGlobal(Module &M, Constant *Initializer, - StringRef OriginalName); - void SetComdatForGlobalMetadata(GlobalVariable *G, GlobalVariable *Metadata, - StringRef InternalSuffix); - IRBuilder<> CreateAsanModuleDtor(Module &M); - - bool ShouldInstrumentGlobal(GlobalVariable *G); - bool ShouldUseMachOGlobalsSection() const; - StringRef getGlobalMetadataSection() const; - void poisonOneInitializer(Function &GlobalInit, GlobalValue *ModuleName); - void createInitializerPoisonCalls(Module &M, GlobalValue *ModuleName); - size_t MinRedzoneSizeForGlobal() const { - return RedzoneSizeForScale(Mapping.Scale); - } - int GetAsanVersion(const Module &M) const; - - GlobalsMetadata GlobalsMD; - bool CompileKernel; - bool Recover; - bool UseGlobalsGC; - bool UseCtorComdat; - Type *IntptrTy; - LLVMContext *C; - Triple TargetTriple; - ShadowMapping Mapping; - Function *AsanPoisonGlobals; - Function *AsanUnpoisonGlobals; - Function *AsanRegisterGlobals; - Function *AsanUnregisterGlobals; - Function *AsanRegisterImageGlobals; - Function *AsanUnregisterImageGlobals; - Function *AsanRegisterElfGlobals; - Function *AsanUnregisterElfGlobals; - - Function *AsanCtorFunction = nullptr; - Function *AsanDtorFunction = nullptr; + StringRef getPassName() const override { return Sanitizer.name(); } + +private: + AddressSanitizerModule Sanitizer; }; +} // end anonymous namespace + // Stack poisoning does not play well with exception handling. // When an exception is thrown, we essentially bypass the code // that unpoisones the stack. This is why the run-time library has @@ -817,7 +696,7 @@ // compiler hoists the load of the shadow value somewhere too high. // This causes asan to report a non-existing bug on 453.povray. // It sounds like an LLVM bug. -struct FunctionStackPoisoner : public InstVisitor { +struct llvm::FunctionStackPoisoner : public InstVisitor { Function &F; AddressSanitizer &ASan; DIBuilder DIB; @@ -878,7 +757,8 @@ copyArgsPassedByValToAllocas(); // Collect alloca, ret, lifetime instructions etc. - for (BasicBlock *BB : depth_first(&F.getEntryBlock())) visit(*BB); + for (BasicBlock *BB : depth_first(&F.getEntryBlock())) + this->visit(*BB); if (AllocaVec.empty() && DynamicAllocaVec.empty()) return false; @@ -1057,18 +937,16 @@ Instruction *ThenTerm, Value *ValueIfFalse); }; -} // end anonymous namespace - -char AddressSanitizer::ID = 0; +char AddressSanitizerLegacy::ID = 0; INITIALIZE_PASS_BEGIN( - AddressSanitizer, "asan", + AddressSanitizerLegacy, "asan", "AddressSanitizer: detects use-after-free and out-of-bounds bugs.", false, false) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_END( - AddressSanitizer, "asan", + AddressSanitizerLegacy, "asan", "AddressSanitizer: detects use-after-free and out-of-bounds bugs.", false, false) @@ -1076,13 +954,13 @@ bool Recover, bool UseAfterScope) { assert(!CompileKernel || Recover); - return new AddressSanitizer(CompileKernel, Recover, UseAfterScope); + return new AddressSanitizerLegacy(CompileKernel, Recover, UseAfterScope); } -char AddressSanitizerModule::ID = 0; +char AddressSanitizerModuleLegacy::ID = 0; INITIALIZE_PASS( - AddressSanitizerModule, "asan-module", + AddressSanitizerModuleLegacy, "asan-module", "AddressSanitizer: detects use-after-free and out-of-bounds bugs." "ModulePass", false, false) @@ -1091,7 +969,7 @@ bool Recover, bool UseGlobalsGC) { assert(!CompileKernel || Recover); - return new AddressSanitizerModule(CompileKernel, Recover, UseGlobalsGC); + return new AddressSanitizerModuleLegacy(CompileKernel, Recover, UseGlobalsGC); } static size_t TypeSizeToSizeIndex(uint32_t TypeSize) { @@ -2268,7 +2146,12 @@ return Version; } -bool AddressSanitizerModule::runOnModule(Module &M) { +PreservedAnalyses AddressSanitizerModule::run(Module &M, + AnalysisManager &AM) { + return instrument(M) ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} + +bool AddressSanitizerModule::instrument(Module &M) { C = &(M.getContext()); int LongSize = M.getDataLayout().getPointerSizeInBits(); IntptrTy = Type::getIntNTy(*C, LongSize); @@ -2316,114 +2199,6 @@ return Changed; } -void AddressSanitizer::initializeCallbacks(Module &M) { - IRBuilder<> IRB(*C); - // Create __asan_report* callbacks. - // IsWrite, TypeSize and Exp are encoded in the function name. - for (int Exp = 0; Exp < 2; Exp++) { - for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) { - const std::string TypeStr = AccessIsWrite ? "store" : "load"; - const std::string ExpStr = Exp ? "exp_" : ""; - const std::string EndingStr = Recover ? "_noabort" : ""; - - SmallVector Args2 = {IntptrTy, IntptrTy}; - SmallVector Args1{1, IntptrTy}; - if (Exp) { - Type *ExpType = Type::getInt32Ty(*C); - Args2.push_back(ExpType); - Args1.push_back(ExpType); - } - AsanErrorCallbackSized[AccessIsWrite][Exp] = - checkSanitizerInterfaceFunction(M.getOrInsertFunction( - kAsanReportErrorTemplate + ExpStr + TypeStr + "_n" + EndingStr, - FunctionType::get(IRB.getVoidTy(), Args2, false))); - - AsanMemoryAccessCallbackSized[AccessIsWrite][Exp] = - checkSanitizerInterfaceFunction(M.getOrInsertFunction( - ClMemoryAccessCallbackPrefix + ExpStr + TypeStr + "N" + EndingStr, - FunctionType::get(IRB.getVoidTy(), Args2, false))); - - for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes; - AccessSizeIndex++) { - const std::string Suffix = TypeStr + itostr(1ULL << AccessSizeIndex); - AsanErrorCallback[AccessIsWrite][Exp][AccessSizeIndex] = - checkSanitizerInterfaceFunction(M.getOrInsertFunction( - kAsanReportErrorTemplate + ExpStr + Suffix + EndingStr, - FunctionType::get(IRB.getVoidTy(), Args1, false))); - - AsanMemoryAccessCallback[AccessIsWrite][Exp][AccessSizeIndex] = - checkSanitizerInterfaceFunction(M.getOrInsertFunction( - ClMemoryAccessCallbackPrefix + ExpStr + Suffix + EndingStr, - FunctionType::get(IRB.getVoidTy(), Args1, false))); - } - } - } - - const std::string MemIntrinCallbackPrefix = - CompileKernel ? std::string("") : ClMemoryAccessCallbackPrefix; - AsanMemmove = checkSanitizerInterfaceFunction(M.getOrInsertFunction( - MemIntrinCallbackPrefix + "memmove", IRB.getInt8PtrTy(), - IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy)); - AsanMemcpy = checkSanitizerInterfaceFunction(M.getOrInsertFunction( - MemIntrinCallbackPrefix + "memcpy", IRB.getInt8PtrTy(), - IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy)); - AsanMemset = checkSanitizerInterfaceFunction(M.getOrInsertFunction( - MemIntrinCallbackPrefix + "memset", IRB.getInt8PtrTy(), - IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy)); - - AsanHandleNoReturnFunc = checkSanitizerInterfaceFunction( - M.getOrInsertFunction(kAsanHandleNoReturnName, IRB.getVoidTy())); - - AsanPtrCmpFunction = checkSanitizerInterfaceFunction(M.getOrInsertFunction( - kAsanPtrCmp, IRB.getVoidTy(), IntptrTy, IntptrTy)); - AsanPtrSubFunction = checkSanitizerInterfaceFunction(M.getOrInsertFunction( - kAsanPtrSub, IRB.getVoidTy(), IntptrTy, IntptrTy)); - // We insert an empty inline asm after __asan_report* to avoid callback merge. - EmptyAsm = InlineAsm::get(FunctionType::get(IRB.getVoidTy(), false), - StringRef(""), StringRef(""), - /*hasSideEffects=*/true); - if (Mapping.InGlobal) - AsanShadowGlobal = M.getOrInsertGlobal("__asan_shadow", - ArrayType::get(IRB.getInt8Ty(), 0)); -} - -// virtual -bool AddressSanitizer::doInitialization(Module &M) { - // Initialize the private fields. No one has accessed them before. - GlobalsMD.init(M); - - C = &(M.getContext()); - LongSize = M.getDataLayout().getPointerSizeInBits(); - IntptrTy = Type::getIntNTy(*C, LongSize); - TargetTriple = Triple(M.getTargetTriple()); - - Mapping = getShadowMapping(TargetTriple, LongSize, CompileKernel); - return true; -} - -bool AddressSanitizer::doFinalization(Module &M) { - GlobalsMD.reset(); - return false; -} - -bool AddressSanitizer::maybeInsertAsanInitAtFunctionEntry(Function &F) { - // For each NSObject descendant having a +load method, this method is invoked - // by the ObjC runtime before any of the static constructors is called. - // Therefore we need to instrument such methods with a call to __asan_init - // at the beginning in order to initialize our runtime before any access to - // the shadow memory. - // We cannot just ignore these methods, because they may call other - // instrumented functions. - if (F.getName().find(" load]") != std::string::npos) { - Function *AsanInitFunction = - declareSanitizerInitFunction(*F.getParent(), kAsanInitName, {}); - IRBuilder<> IRB(&F.front(), F.front().begin()); - IRB.CreateCall(AsanInitFunction, {}); - return true; - } - return false; -} - void AddressSanitizer::maybeInsertDynamicShadowAtFunctionEntry(Function &F) { // Generate code only when dynamic addressing is needed. if (Mapping.Offset != kDynamicShadowSentinel) @@ -2479,10 +2254,44 @@ } } -bool AddressSanitizer::runOnFunction(Function &F) { - if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return false; - if (!ClDebugFunc.empty() && ClDebugFunc == F.getName()) return false; - if (F.getName().startswith("__asan_")) return false; +AddressSanitizer::AddressSanitizer(bool CompileKernel, bool Recover, + bool UseAfterScope) + : UseAfterScope(UseAfterScope || ClUseAfterScope) { + this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover; + this->CompileKernel = + ClEnableKasan.getNumOccurrences() > 0 ? ClEnableKasan : CompileKernel; +} + +PreservedAnalyses AddressSanitizer::run(Function &F, + AnalysisManager &AM) { + DominatorTree *DT = &AM.getResult(F); + const TargetLibraryInfo *TLI = &AM.getResult(F); + return instrument(F, DT, TLI) ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} + +bool AddressSanitizer::instrument(Function &F, DominatorTree *DTree, + const TargetLibraryInfo *TLI) { + if (!Inited) { + // Initialize the private fields. No one has accessed them before. + Module &M = *F.getParent(); + GlobalsMD.init(M); + + C = &(M.getContext()); + LongSize = M.getDataLayout().getPointerSizeInBits(); + IntptrTy = Type::getIntNTy(*C, LongSize); + TargetTriple = Triple(M.getTargetTriple()); + + Mapping = getShadowMapping(TargetTriple, LongSize, CompileKernel); + Inited = true; + } + + if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) + return false; + if (!ClDebugFunc.empty() && ClDebugFunc == F.getName()) + return false; + if (F.getName().startswith("__asan_")) + return false; bool FunctionModified = false; @@ -2493,12 +2302,13 @@ FunctionModified = true; // Leave if the function doesn't need instrumentation. - if (!F.hasFnAttribute(Attribute::SanitizeAddress)) return FunctionModified; + if (!F.hasFnAttribute(Attribute::SanitizeAddress)) + return FunctionModified; LLVM_DEBUG(dbgs() << "ASAN instrumenting:\n" << F << "\n"); initializeCallbacks(*F.getParent()); - DT = &getAnalysis().getDomTree(); + DT = DTree; FunctionStateRAII CleanupObj(this); @@ -2519,8 +2329,6 @@ bool IsWrite; unsigned Alignment; uint64_t TypeSize; - const TargetLibraryInfo *TLI = - &getAnalysis().getTLI(); // Fill the set of memory operations to instrument. for (auto &BB : F) { @@ -2614,6 +2422,13 @@ return FunctionModified; } +bool AddressSanitizerLegacy::runOnFunction(Function &F) { + DominatorTree *DTree = &getAnalysis().getDomTree(); + const TargetLibraryInfo *TLI = + &getAnalysis().getTLI(); + return Sanitizer.instrument(F, DTree, TLI); +} + // Workaround for bug 11395: we don't want to instrument stack in functions // with large assembly blobs (32-bit only), otherwise reg alloc may crash. // FIXME: remove once the bug 11395 is fixed. Index: lib/Transforms/Instrumentation/Instrumentation.cpp =================================================================== --- lib/Transforms/Instrumentation/Instrumentation.cpp +++ lib/Transforms/Instrumentation/Instrumentation.cpp @@ -56,8 +56,8 @@ /// initializeInstrumentation - Initialize all passes in the TransformUtils /// library. void llvm::initializeInstrumentation(PassRegistry &Registry) { - initializeAddressSanitizerPass(Registry); - initializeAddressSanitizerModulePass(Registry); + initializeAddressSanitizerLegacyPass(Registry); + initializeAddressSanitizerModuleLegacyPass(Registry); initializeBoundsCheckingLegacyPassPass(Registry); initializeControlHeightReductionLegacyPassPass(Registry); initializeGCOVProfilerLegacyPassPass(Registry); Index: test/Instrumentation/AddressSanitizer/basic.ll =================================================================== --- test/Instrumentation/AddressSanitizer/basic.ll +++ test/Instrumentation/AddressSanitizer/basic.ll @@ -1,7 +1,9 @@ ; Test basic address sanitizer instrumentation. ; ; RUN: opt < %s -asan -asan-module -S | FileCheck --check-prefixes=CHECK,CHECK-S3 %s +; RUN: opt < %s -passes='function(asan),module(asan-module)' -S | FileCheck --check-prefixes=CHECK,CHECK-S3 %s ; RUN: opt < %s -asan -asan-module -asan-mapping-scale=5 -S | FileCheck --check-prefixes=CHECK,CHECK-S5 %s +; RUN: opt < %s -passes='function(asan),module(asan-module)' -asan-mapping-scale=5 -S | FileCheck --check-prefixes=CHECK,CHECK-S5 %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" target triple = "x86_64-unknown-linux-gnu"