diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp')
-rw-r--r-- | contrib/llvm-project/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp | 401 |
1 files changed, 245 insertions, 156 deletions
diff --git a/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp index efb621cde906..a0e63bf12400 100644 --- a/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -15,11 +15,15 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/Analysis/DomTreeUpdater.h" #include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/PostDominators.h" +#include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/StackSafetyAnalysis.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" @@ -49,6 +53,8 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/Instrumentation/AddressSanitizerCommon.h" @@ -58,6 +64,7 @@ #include "llvm/Transforms/Utils/ModuleUtils.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" #include <optional> +#include <random> using namespace llvm; @@ -177,6 +184,18 @@ static cl::opt<bool> ClWithTls( "platforms that support this"), cl::Hidden, cl::init(true)); +static cl::opt<int> ClHotPercentileCutoff("hwasan-percentile-cutoff-hot", + cl::desc("Hot percentile cuttoff.")); + +static cl::opt<float> + ClRandomSkipRate("hwasan-random-rate", + cl::desc("Probability value in the range [0.0, 1.0] " + "to keep instrumentation of a function.")); + +STATISTIC(NumTotalFuncs, "Number of total funcs"); +STATISTIC(NumInstrumentedFuncs, "Number of instrumented funcs"); +STATISTIC(NumNoProfileSummaryFuncs, "Number of funcs without PS"); + // Mode for selecting how to insert frame record info into the stack ring // buffer. enum RecordStackHistoryMode { @@ -236,6 +255,10 @@ static cl::opt<bool> ClUsePageAliases("hwasan-experimental-use-page-aliases", namespace { +template <typename T> T optOr(cl::opt<T> &Opt, T Other) { + return Opt.getNumOccurrences() ? Opt : Other; +} + bool shouldUsePageAliases(const Triple &TargetTriple) { return ClUsePageAliases && TargetTriple.getArch() == Triple::x86_64; } @@ -245,14 +268,11 @@ bool shouldInstrumentStack(const Triple &TargetTriple) { } bool shouldInstrumentWithCalls(const Triple &TargetTriple) { - return ClInstrumentWithCalls.getNumOccurrences() - ? ClInstrumentWithCalls - : TargetTriple.getArch() == Triple::x86_64; + return optOr(ClInstrumentWithCalls, TargetTriple.getArch() == Triple::x86_64); } bool mightUseStackSafetyAnalysis(bool DisableOptimization) { - return ClUseStackSafety.getNumOccurrences() ? ClUseStackSafety - : !DisableOptimization; + return optOr(ClUseStackSafety, !DisableOptimization); } bool shouldUseStackSafetyAnalysis(const Triple &TargetTriple, @@ -272,10 +292,10 @@ public: HWAddressSanitizer(Module &M, bool CompileKernel, bool Recover, const StackSafetyGlobalInfo *SSI) : M(M), SSI(SSI) { - this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover; - this->CompileKernel = ClEnableKhwasan.getNumOccurrences() > 0 - ? ClEnableKhwasan - : CompileKernel; + this->Recover = optOr(ClRecover, Recover); + this->CompileKernel = optOr(ClEnableKhwasan, CompileKernel); + this->Rng = ClRandomSkipRate.getNumOccurrences() ? M.createRNG(DEBUG_TYPE) + : nullptr; initializeModule(); } @@ -290,8 +310,9 @@ private: Value *PtrTag = nullptr; Value *MemTag = nullptr; }; - void setSSI(const StackSafetyGlobalInfo *S) { SSI = S; } + bool selectiveInstrumentationShouldSkip(Function &F, + FunctionAnalysisManager &FAM) const; void initializeModule(); void createHwasanCtorComdat(); @@ -316,13 +337,17 @@ private: unsigned AccessSizeIndex, Instruction *InsertBefore, DomTreeUpdater &DTU, LoopInfo *LI); - bool ignoreMemIntrinsic(MemIntrinsic *MI); + bool ignoreMemIntrinsic(OptimizationRemarkEmitter &ORE, MemIntrinsic *MI); void instrumentMemIntrinsic(MemIntrinsic *MI); bool instrumentMemAccess(InterestingMemoryOperand &O, DomTreeUpdater &DTU, LoopInfo *LI); - bool ignoreAccess(Instruction *Inst, Value *Ptr); + bool ignoreAccessWithoutRemark(Instruction *Inst, Value *Ptr); + bool ignoreAccess(OptimizationRemarkEmitter &ORE, Instruction *Inst, + Value *Ptr); + void getInterestingMemoryOperands( - Instruction *I, const TargetLibraryInfo &TLI, + OptimizationRemarkEmitter &ORE, Instruction *I, + const TargetLibraryInfo &TLI, SmallVectorImpl<InterestingMemoryOperand> &Interesting); void tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, size_t Size); @@ -331,14 +356,13 @@ private: bool instrumentStack(memtag::StackInfo &Info, Value *StackTag, Value *UARTag, const DominatorTree &DT, const PostDominatorTree &PDT, const LoopInfo &LI); - Value *readRegister(IRBuilder<> &IRB, StringRef Name); bool instrumentLandingPads(SmallVectorImpl<Instruction *> &RetVec); Value *getNextTagWithCall(IRBuilder<> &IRB); Value *getStackBaseTag(IRBuilder<> &IRB); Value *getAllocaTag(IRBuilder<> &IRB, Value *StackTag, unsigned AllocaNo); Value *getUARTag(IRBuilder<> &IRB); - Value *getHwasanThreadSlotPtr(IRBuilder<> &IRB, Type *Ty); + Value *getHwasanThreadSlotPtr(IRBuilder<> &IRB); Value *applyTagMask(IRBuilder<> &IRB, Value *OldTag); unsigned retagMask(unsigned AllocaNo); @@ -347,8 +371,7 @@ private: void instrumentGlobal(GlobalVariable *GV, uint8_t Tag); void instrumentGlobals(); - Value *getPC(IRBuilder<> &IRB); - Value *getSP(IRBuilder<> &IRB); + Value *getCachedFP(IRBuilder<> &IRB); Value *getFrameRecordInfo(IRBuilder<> &IRB); void instrumentPersonalityFunctions(); @@ -357,6 +380,7 @@ private: Module &M; const StackSafetyGlobalInfo *SSI; Triple TargetTriple; + std::unique_ptr<RandomNumberGenerator> Rng; /// This struct defines the shadow mapping using the rule: /// shadow = (mem >> Scale) + Offset. @@ -383,10 +407,10 @@ private: ShadowMapping Mapping; Type *VoidTy = Type::getVoidTy(M.getContext()); - Type *IntptrTy; - PointerType *PtrTy; - Type *Int8Ty; - Type *Int32Ty; + Type *IntptrTy = M.getDataLayout().getIntPtrType(M.getContext()); + PointerType *PtrTy = PointerType::getUnqual(M.getContext()); + Type *Int8Ty = Type::getInt8Ty(M.getContext()); + Type *Int32Ty = Type::getInt32Ty(M.getContext()); Type *Int64Ty = Type::getInt64Ty(M.getContext()); bool CompileKernel; @@ -397,6 +421,7 @@ private: bool InstrumentLandingPads; bool InstrumentWithCalls; bool InstrumentStack; + bool InstrumentGlobals; bool DetectUseAfterScope; bool UsePageAliases; bool UseMatchAllCallback; @@ -422,7 +447,7 @@ private: Value *ShadowBase = nullptr; Value *StackBaseTag = nullptr; - Value *CachedSP = nullptr; + Value *CachedFP = nullptr; GlobalValue *ThreadPtrGlobal = nullptr; }; @@ -567,8 +592,6 @@ void HWAddressSanitizer::createHwasanCtorComdat() { /// inserts a call to __hwasan_init to the module's constructor list. void HWAddressSanitizer::initializeModule() { LLVM_DEBUG(dbgs() << "Init " << M.getName() << "\n"); - auto &DL = M.getDataLayout(); - TargetTriple = Triple(M.getTargetTriple()); // x86_64 currently has two modes: @@ -586,10 +609,6 @@ void HWAddressSanitizer::initializeModule() { C = &(M.getContext()); IRBuilder<> IRB(*C); - IntptrTy = IRB.getIntPtrTy(DL); - PtrTy = IRB.getPtrTy(); - Int8Ty = IRB.getInt8Ty(); - Int32Ty = IRB.getInt32Ty(); HwasanCtorFunction = nullptr; @@ -599,19 +618,14 @@ void HWAddressSanitizer::initializeModule() { bool NewRuntime = !TargetTriple.isAndroid() || !TargetTriple.isAndroidVersionLT(30); - UseShortGranules = - ClUseShortGranules.getNumOccurrences() ? ClUseShortGranules : NewRuntime; - OutlinedChecks = - (TargetTriple.isAArch64() || TargetTriple.isRISCV64()) && - TargetTriple.isOSBinFormatELF() && - (ClInlineAllChecks.getNumOccurrences() ? !ClInlineAllChecks : !Recover); + UseShortGranules = optOr(ClUseShortGranules, NewRuntime); + OutlinedChecks = (TargetTriple.isAArch64() || TargetTriple.isRISCV64()) && + TargetTriple.isOSBinFormatELF() && + !optOr(ClInlineAllChecks, Recover); - InlineFastPath = - (ClInlineFastPathChecks.getNumOccurrences() - ? ClInlineFastPathChecks - : !(TargetTriple.isAndroid() || - TargetTriple.isOSFuchsia())); // These platforms may prefer less - // inlining to reduce binary size. + // These platforms may prefer less inlining to reduce binary size. + InlineFastPath = optOr(ClInlineFastPathChecks, !(TargetTriple.isAndroid() || + TargetTriple.isOSFuchsia())); if (ClMatchAllTag.getNumOccurrences()) { if (ClMatchAllTag != -1) { @@ -623,22 +637,19 @@ void HWAddressSanitizer::initializeModule() { UseMatchAllCallback = !CompileKernel && MatchAllTag.has_value(); // If we don't have personality function support, fall back to landing pads. - InstrumentLandingPads = ClInstrumentLandingPads.getNumOccurrences() - ? ClInstrumentLandingPads - : !NewRuntime; + InstrumentLandingPads = optOr(ClInstrumentLandingPads, !NewRuntime); + + InstrumentGlobals = + !CompileKernel && !UsePageAliases && optOr(ClGlobals, NewRuntime); if (!CompileKernel) { createHwasanCtorComdat(); - bool InstrumentGlobals = - ClGlobals.getNumOccurrences() ? ClGlobals : NewRuntime; - if (InstrumentGlobals && !UsePageAliases) + if (InstrumentGlobals) instrumentGlobals(); bool InstrumentPersonalityFunctions = - ClInstrumentPersonalityFunctions.getNumOccurrences() - ? ClInstrumentPersonalityFunctions - : NewRuntime; + optOr(ClInstrumentPersonalityFunctions, NewRuntime); if (InstrumentPersonalityFunctions) instrumentPersonalityFunctions(); } @@ -758,7 +769,8 @@ Value *HWAddressSanitizer::getShadowNonTls(IRBuilder<> &IRB) { return IRB.CreateLoad(PtrTy, GlobalDynamicAddress); } -bool HWAddressSanitizer::ignoreAccess(Instruction *Inst, Value *Ptr) { +bool HWAddressSanitizer::ignoreAccessWithoutRemark(Instruction *Inst, + Value *Ptr) { // Do not instrument accesses from different address spaces; we cannot deal // with them. Type *PtrTy = cast<PointerType>(Ptr->getType()->getScalarType()); @@ -778,11 +790,33 @@ bool HWAddressSanitizer::ignoreAccess(Instruction *Inst, Value *Ptr) { if (SSI && SSI->stackAccessIsSafe(*Inst)) return true; } + + if (isa<GlobalVariable>(getUnderlyingObject(Ptr))) { + if (!InstrumentGlobals) + return true; + // TODO: Optimize inbound global accesses, like Asan `instrumentMop`. + } + return false; } +bool HWAddressSanitizer::ignoreAccess(OptimizationRemarkEmitter &ORE, + Instruction *Inst, Value *Ptr) { + bool Ignored = ignoreAccessWithoutRemark(Inst, Ptr); + if (Ignored) { + ORE.emit( + [&]() { return OptimizationRemark(DEBUG_TYPE, "ignoreAccess", Inst); }); + } else { + ORE.emit([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "ignoreAccess", Inst); + }); + } + return Ignored; +} + void HWAddressSanitizer::getInterestingMemoryOperands( - Instruction *I, const TargetLibraryInfo &TLI, + OptimizationRemarkEmitter &ORE, Instruction *I, + const TargetLibraryInfo &TLI, SmallVectorImpl<InterestingMemoryOperand> &Interesting) { // Skip memory accesses inserted by another instrumentation. if (I->hasMetadata(LLVMContext::MD_nosanitize)) @@ -793,22 +827,22 @@ void HWAddressSanitizer::getInterestingMemoryOperands( return; if (LoadInst *LI = dyn_cast<LoadInst>(I)) { - if (!ClInstrumentReads || ignoreAccess(I, LI->getPointerOperand())) + if (!ClInstrumentReads || ignoreAccess(ORE, I, LI->getPointerOperand())) return; Interesting.emplace_back(I, LI->getPointerOperandIndex(), false, LI->getType(), LI->getAlign()); } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) { - if (!ClInstrumentWrites || ignoreAccess(I, SI->getPointerOperand())) + if (!ClInstrumentWrites || ignoreAccess(ORE, I, SI->getPointerOperand())) return; Interesting.emplace_back(I, SI->getPointerOperandIndex(), true, SI->getValueOperand()->getType(), SI->getAlign()); } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) { - if (!ClInstrumentAtomics || ignoreAccess(I, RMW->getPointerOperand())) + if (!ClInstrumentAtomics || ignoreAccess(ORE, I, RMW->getPointerOperand())) return; Interesting.emplace_back(I, RMW->getPointerOperandIndex(), true, RMW->getValOperand()->getType(), std::nullopt); } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) { - if (!ClInstrumentAtomics || ignoreAccess(I, XCHG->getPointerOperand())) + if (!ClInstrumentAtomics || ignoreAccess(ORE, I, XCHG->getPointerOperand())) return; Interesting.emplace_back(I, XCHG->getPointerOperandIndex(), true, XCHG->getCompareOperand()->getType(), @@ -816,7 +850,7 @@ void HWAddressSanitizer::getInterestingMemoryOperands( } else if (auto *CI = dyn_cast<CallInst>(I)) { for (unsigned ArgNo = 0; ArgNo < CI->arg_size(); ArgNo++) { if (!ClInstrumentByval || !CI->isByValArgument(ArgNo) || - ignoreAccess(I, CI->getArgOperand(ArgNo))) + ignoreAccess(ORE, I, CI->getArgOperand(ArgNo))) continue; Type *Ty = CI->getParamByValType(ArgNo); Interesting.emplace_back(I, ArgNo, false, Ty, Align(1)); @@ -898,7 +932,7 @@ HWAddressSanitizer::insertShadowTagCheck(Value *Ptr, Instruction *InsertBefore, R.TagMismatchTerm = SplitBlockAndInsertIfThen( TagMismatch, InsertBefore, false, - MDBuilder(*C).createBranchWeights(1, 100000), &DTU, LI); + MDBuilder(*C).createUnlikelyBranchWeights(), &DTU, LI); return R; } @@ -917,11 +951,33 @@ void HWAddressSanitizer::instrumentMemAccessOutline(Value *Ptr, bool IsWrite, IRBuilder<> IRB(InsertBefore); Module *M = IRB.GetInsertBlock()->getParent()->getParent(); - IRB.CreateCall(Intrinsic::getDeclaration( - M, UseShortGranules - ? Intrinsic::hwasan_check_memaccess_shortgranules - : Intrinsic::hwasan_check_memaccess), - {ShadowBase, Ptr, ConstantInt::get(Int32Ty, AccessInfo)}); + bool useFixedShadowIntrinsic = false; + // The memaccess fixed shadow intrinsic is only supported on AArch64, + // which allows a 16-bit immediate to be left-shifted by 32. + // Since kShadowBaseAlignment == 32, and Linux by default will not + // mmap above 48-bits, practically any valid shadow offset is + // representable. + // In particular, an offset of 4TB (1024 << 32) is representable, and + // ought to be good enough for anybody. + if (TargetTriple.isAArch64() && Mapping.Offset != kDynamicShadowSentinel) { + uint16_t offset_shifted = Mapping.Offset >> 32; + useFixedShadowIntrinsic = (uint64_t)offset_shifted << 32 == Mapping.Offset; + } + + if (useFixedShadowIntrinsic) + IRB.CreateCall( + Intrinsic::getDeclaration( + M, UseShortGranules + ? Intrinsic::hwasan_check_memaccess_shortgranules_fixedshadow + : Intrinsic::hwasan_check_memaccess_fixedshadow), + {Ptr, ConstantInt::get(Int32Ty, AccessInfo), + ConstantInt::get(Int64Ty, Mapping.Offset)}); + else + IRB.CreateCall(Intrinsic::getDeclaration( + M, UseShortGranules + ? Intrinsic::hwasan_check_memaccess_shortgranules + : Intrinsic::hwasan_check_memaccess), + {ShadowBase, Ptr, ConstantInt::get(Int32Ty, AccessInfo)}); } void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite, @@ -939,7 +995,7 @@ void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite, IRB.CreateICmpUGT(TCI.MemTag, ConstantInt::get(Int8Ty, 15)); Instruction *CheckFailTerm = SplitBlockAndInsertIfThen( OutOfShortGranuleTagRange, TCI.TagMismatchTerm, !Recover, - MDBuilder(*C).createBranchWeights(1, 100000), &DTU, LI); + MDBuilder(*C).createUnlikelyBranchWeights(), &DTU, LI); IRB.SetInsertPoint(TCI.TagMismatchTerm); Value *PtrLowBits = IRB.CreateTrunc(IRB.CreateAnd(TCI.PtrLong, 15), Int8Ty); @@ -947,7 +1003,7 @@ void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite, PtrLowBits, ConstantInt::get(Int8Ty, (1 << AccessSizeIndex) - 1)); Value *PtrLowBitsOOB = IRB.CreateICmpUGE(PtrLowBits, TCI.MemTag); SplitBlockAndInsertIfThen(PtrLowBitsOOB, TCI.TagMismatchTerm, false, - MDBuilder(*C).createBranchWeights(1, 100000), &DTU, + MDBuilder(*C).createUnlikelyBranchWeights(), &DTU, LI, CheckFailTerm->getParent()); IRB.SetInsertPoint(TCI.TagMismatchTerm); @@ -956,7 +1012,7 @@ void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite, Value *InlineTag = IRB.CreateLoad(Int8Ty, InlineTagAddr); Value *InlineTagMismatch = IRB.CreateICmpNE(TCI.PtrTag, InlineTag); SplitBlockAndInsertIfThen(InlineTagMismatch, TCI.TagMismatchTerm, false, - MDBuilder(*C).createBranchWeights(1, 100000), &DTU, + MDBuilder(*C).createUnlikelyBranchWeights(), &DTU, LI, CheckFailTerm->getParent()); IRB.SetInsertPoint(CheckFailTerm); @@ -999,13 +1055,14 @@ void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite, ->setSuccessor(0, TCI.TagMismatchTerm->getParent()); } -bool HWAddressSanitizer::ignoreMemIntrinsic(MemIntrinsic *MI) { +bool HWAddressSanitizer::ignoreMemIntrinsic(OptimizationRemarkEmitter &ORE, + MemIntrinsic *MI) { if (MemTransferInst *MTI = dyn_cast<MemTransferInst>(MI)) { - return (!ClInstrumentWrites || ignoreAccess(MTI, MTI->getDest())) && - (!ClInstrumentReads || ignoreAccess(MTI, MTI->getSource())); + return (!ClInstrumentWrites || ignoreAccess(ORE, MTI, MTI->getDest())) && + (!ClInstrumentReads || ignoreAccess(ORE, MTI, MTI->getSource())); } if (isa<MemSetInst>(MI)) - return !ClInstrumentWrites || ignoreAccess(MI, MI->getDest()); + return !ClInstrumentWrites || ignoreAccess(ORE, MI, MI->getDest()); return false; } @@ -1148,10 +1205,10 @@ Value *HWAddressSanitizer::getStackBaseTag(IRBuilder<> &IRB) { // Extract some entropy from the stack pointer for the tags. // Take bits 20..28 (ASLR entropy) and xor with bits 0..8 (these differ // between functions). - Value *StackPointerLong = getSP(IRB); + Value *FramePointerLong = getCachedFP(IRB); Value *StackTag = - applyTagMask(IRB, IRB.CreateXor(StackPointerLong, - IRB.CreateLShr(StackPointerLong, 20))); + applyTagMask(IRB, IRB.CreateXor(FramePointerLong, + IRB.CreateLShr(FramePointerLong, 20))); StackTag->setName("hwasan.stack.base.tag"); return StackTag; } @@ -1165,9 +1222,9 @@ Value *HWAddressSanitizer::getAllocaTag(IRBuilder<> &IRB, Value *StackTag, } Value *HWAddressSanitizer::getUARTag(IRBuilder<> &IRB) { - Value *StackPointerLong = getSP(IRB); + Value *FramePointerLong = getCachedFP(IRB); Value *UARTag = - applyTagMask(IRB, IRB.CreateLShr(StackPointerLong, PointerTagShift)); + applyTagMask(IRB, IRB.CreateLShr(FramePointerLong, PointerTagShift)); UARTag->setName("hwasan.uar.tag"); return UARTag; @@ -1210,57 +1267,37 @@ Value *HWAddressSanitizer::untagPointer(IRBuilder<> &IRB, Value *PtrLong) { return UntaggedPtrLong; } -Value *HWAddressSanitizer::getHwasanThreadSlotPtr(IRBuilder<> &IRB, Type *Ty) { - Module *M = IRB.GetInsertBlock()->getParent()->getParent(); - if (TargetTriple.isAArch64() && TargetTriple.isAndroid()) { - // Android provides a fixed TLS slot for sanitizers. See TLS_SLOT_SANITIZER - // in Bionic's libc/private/bionic_tls.h. - Function *ThreadPointerFunc = - Intrinsic::getDeclaration(M, Intrinsic::thread_pointer); - return IRB.CreateConstGEP1_32(Int8Ty, IRB.CreateCall(ThreadPointerFunc), - 0x30); - } - if (ThreadPtrGlobal) - return ThreadPtrGlobal; - - return nullptr; -} - -Value *HWAddressSanitizer::getPC(IRBuilder<> &IRB) { - if (TargetTriple.getArch() == Triple::aarch64) - return readRegister(IRB, "pc"); - return IRB.CreatePtrToInt(IRB.GetInsertBlock()->getParent(), IntptrTy); +Value *HWAddressSanitizer::getHwasanThreadSlotPtr(IRBuilder<> &IRB) { + // Android provides a fixed TLS slot for sanitizers. See TLS_SLOT_SANITIZER + // in Bionic's libc/platform/bionic/tls_defines.h. + constexpr int SanitizerSlot = 6; + if (TargetTriple.isAArch64() && TargetTriple.isAndroid()) + return memtag::getAndroidSlotPtr(IRB, SanitizerSlot); + return ThreadPtrGlobal; } -Value *HWAddressSanitizer::getSP(IRBuilder<> &IRB) { - if (!CachedSP) { - // FIXME: use addressofreturnaddress (but implement it in aarch64 backend - // first). - Function *F = IRB.GetInsertBlock()->getParent(); - Module *M = F->getParent(); - auto *GetStackPointerFn = Intrinsic::getDeclaration( - M, Intrinsic::frameaddress, - IRB.getPtrTy(M->getDataLayout().getAllocaAddrSpace())); - CachedSP = IRB.CreatePtrToInt( - IRB.CreateCall(GetStackPointerFn, {Constant::getNullValue(Int32Ty)}), - IntptrTy); - } - return CachedSP; +Value *HWAddressSanitizer::getCachedFP(IRBuilder<> &IRB) { + if (!CachedFP) + CachedFP = memtag::getFP(IRB); + return CachedFP; } Value *HWAddressSanitizer::getFrameRecordInfo(IRBuilder<> &IRB) { // Prepare ring buffer data. - Value *PC = getPC(IRB); - Value *SP = getSP(IRB); + Value *PC = memtag::getPC(TargetTriple, IRB); + Value *FP = getCachedFP(IRB); - // Mix SP and PC. + // Mix FP and PC. // Assumptions: // PC is 0x0000PPPPPPPPPPPP (48 bits are meaningful, others are zero) - // SP is 0xsssssssssssSSSS0 (4 lower bits are zero) - // We only really need ~20 lower non-zero bits (SSSS), so we mix like this: - // 0xSSSSPPPPPPPPPPPP - SP = IRB.CreateShl(SP, 44); - return IRB.CreateOr(PC, SP); + // FP is 0xfffffffffffFFFF0 (4 lower bits are zero) + // We only really need ~20 lower non-zero bits (FFFF), so we mix like this: + // 0xFFFFPPPPPPPPPPPP + // + // FP works because in AArch64FrameLowering::getFrameIndexReference, we + // prefer FP-relative offsets for functions compiled with HWASan. + FP = IRB.CreateShl(FP, 44); + return IRB.CreateOr(PC, FP); } void HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord) { @@ -1278,7 +1315,7 @@ void HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord) { auto getThreadLongMaybeUntagged = [&]() { if (!SlotPtr) - SlotPtr = getHwasanThreadSlotPtr(IRB, IntptrTy); + SlotPtr = getHwasanThreadSlotPtr(IRB); if (!ThreadLong) ThreadLong = IRB.CreateLoad(IntptrTy, SlotPtr); // Extract the address field from ThreadLong. Unnecessary on AArch64 with @@ -1314,6 +1351,22 @@ void HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord) { // The use of AShr instead of LShr is due to // https://bugs.llvm.org/show_bug.cgi?id=39030 // Runtime library makes sure not to use the highest bit. + // + // Mechanical proof of this address calculation can be found at: + // https://github.com/google/sanitizers/blob/master/hwaddress-sanitizer/prove_hwasanwrap.smt2 + // + // Example of the wrap case for N = 1 + // Pointer: 0x01AAAAAAAAAAAFF8 + // + + // 0x0000000000000008 + // = + // 0x01AAAAAAAAAAB000 + // & + // WrapMask: 0xFFFFFFFFFFFFF000 + // = + // 0x01AAAAAAAAAAA000 + // + // Then the WrapMask will be a no-op until the next wrap case. Value *WrapMask = IRB.CreateXor( IRB.CreateShl(IRB.CreateAShr(ThreadLong, 56), 12, "", true, true), ConstantInt::get(IntptrTy, (uint64_t)-1)); @@ -1345,32 +1398,18 @@ void HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord) { } } -Value *HWAddressSanitizer::readRegister(IRBuilder<> &IRB, StringRef Name) { - Module *M = IRB.GetInsertBlock()->getParent()->getParent(); - Function *ReadRegister = - Intrinsic::getDeclaration(M, Intrinsic::read_register, IntptrTy); - MDNode *MD = MDNode::get(*C, {MDString::get(*C, Name)}); - Value *Args[] = {MetadataAsValue::get(*C, MD)}; - return IRB.CreateCall(ReadRegister, Args); -} - bool HWAddressSanitizer::instrumentLandingPads( SmallVectorImpl<Instruction *> &LandingPadVec) { for (auto *LP : LandingPadVec) { - IRBuilder<> IRB(LP->getNextNode()); + IRBuilder<> IRB(LP->getNextNonDebugInstruction()); IRB.CreateCall( HwasanHandleVfork, - {readRegister(IRB, (TargetTriple.getArch() == Triple::x86_64) ? "rsp" - : "sp")}); + {memtag::readRegister( + IRB, (TargetTriple.getArch() == Triple::x86_64) ? "rsp" : "sp")}); } return true; } -static bool isLifetimeIntrinsic(Value *V) { - auto *II = dyn_cast<IntrinsicInst>(V); - return II && II->isLifetimeStartOrEnd(); -} - bool HWAddressSanitizer::instrumentStack(memtag::StackInfo &SInfo, Value *StackTag, Value *UARTag, const DominatorTree &DT, @@ -1387,7 +1426,7 @@ bool HWAddressSanitizer::instrumentStack(memtag::StackInfo &SInfo, auto N = I++; auto *AI = KV.first; memtag::AllocaInfo &Info = KV.second; - IRBuilder<> IRB(AI->getNextNode()); + IRBuilder<> IRB(AI->getNextNonDebugInstruction()); // Replace uses of the alloca with tagged address. Value *Tag = getAllocaTag(IRB, StackTag, N); @@ -1422,20 +1461,11 @@ bool HWAddressSanitizer::instrumentStack(memtag::StackInfo &SInfo, AI->replaceUsesWithIf(Replacement, [AICast, AILong](const Use &U) { auto *User = U.getUser(); - return User != AILong && User != AICast && !isLifetimeIntrinsic(User); + return User != AILong && User != AICast && + !memtag::isLifetimeIntrinsic(User); }); - for (auto *DDI : Info.DbgVariableIntrinsics) { - // Prepend "tag_offset, N" to the dwarf expression. - // Tag offset logically applies to the alloca pointer, and it makes sense - // to put it at the beginning of the expression. - SmallVector<uint64_t, 8> NewOps = {dwarf::DW_OP_LLVM_tag_offset, - retagMask(N)}; - for (size_t LocNo = 0; LocNo < DDI->getNumVariableLocationOps(); ++LocNo) - if (DDI->getVariableLocationOp(LocNo) == AI) - DDI->setExpression(DIExpression::appendOpsToArg(DDI->getExpression(), - NewOps, LocNo)); - } + memtag::annotateDebugRecords(Info, retagMask(N)); auto TagEnd = [&](Instruction *Node) { IRB.SetInsertPoint(Node); @@ -1450,10 +1480,10 @@ bool HWAddressSanitizer::instrumentStack(memtag::StackInfo &SInfo, // function return. Work around this by always untagging at every return // statement if return_twice functions are called. bool StandardLifetime = + !SInfo.CallsReturnTwice && SInfo.UnrecognizedLifetimes.empty() && memtag::isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd, &DT, - &LI, ClMaxLifetimes) && - !SInfo.CallsReturnTwice; + &LI, ClMaxLifetimes); if (DetectUseAfterScope && StandardLifetime) { IntrinsicInst *Start = Info.LifetimeStart[0]; IRB.SetInsertPoint(Start->getNextNode()); @@ -1481,6 +1511,44 @@ bool HWAddressSanitizer::instrumentStack(memtag::StackInfo &SInfo, return true; } +static void emitRemark(const Function &F, OptimizationRemarkEmitter &ORE, + bool Skip) { + if (Skip) { + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "Skip", &F) + << "Skipped: F=" << ore::NV("Function", &F); + }); + } else { + ORE.emit([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "Sanitize", &F) + << "Sanitized: F=" << ore::NV("Function", &F); + }); + } +} + +bool HWAddressSanitizer::selectiveInstrumentationShouldSkip( + Function &F, FunctionAnalysisManager &FAM) const { + bool Skip = [&]() { + if (ClRandomSkipRate.getNumOccurrences()) { + std::bernoulli_distribution D(ClRandomSkipRate); + return !D(*Rng); + } + if (!ClHotPercentileCutoff.getNumOccurrences()) + return false; + auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F); + ProfileSummaryInfo *PSI = + MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent()); + if (!PSI || !PSI->hasProfileSummary()) { + ++NumNoProfileSummaryFuncs; + return false; + } + return PSI->isFunctionHotInCallGraphNthPercentile( + ClHotPercentileCutoff, &F, FAM.getResult<BlockFrequencyAnalysis>(F)); + }(); + emitRemark(F, FAM.getResult<OptimizationRemarkEmitterAnalysis>(F), Skip); + return Skip; +} + void HWAddressSanitizer::sanitizeFunction(Function &F, FunctionAnalysisManager &FAM) { if (&F == HwasanCtorFunction) @@ -1489,6 +1557,19 @@ void HWAddressSanitizer::sanitizeFunction(Function &F, if (!F.hasFnAttribute(Attribute::SanitizeHWAddress)) return; + if (F.empty()) + return; + + NumTotalFuncs++; + + OptimizationRemarkEmitter &ORE = + FAM.getResult<OptimizationRemarkEmitterAnalysis>(F); + + if (selectiveInstrumentationShouldSkip(F, FAM)) + return; + + NumInstrumentedFuncs++; + LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n"); SmallVector<InterestingMemoryOperand, 16> OperandsToInstrument; @@ -1505,10 +1586,10 @@ void HWAddressSanitizer::sanitizeFunction(Function &F, if (InstrumentLandingPads && isa<LandingPadInst>(Inst)) LandingPadVec.push_back(&Inst); - getInterestingMemoryOperands(&Inst, TLI, OperandsToInstrument); + getInterestingMemoryOperands(ORE, &Inst, TLI, OperandsToInstrument); if (MemIntrinsic *MI = dyn_cast<MemIntrinsic>(&Inst)) - if (!ignoreMemIntrinsic(MI)) + if (!ignoreMemIntrinsic(ORE, MI)) IntrinToInstrument.push_back(MI); } @@ -1532,8 +1613,16 @@ void HWAddressSanitizer::sanitizeFunction(Function &F, assert(!ShadowBase); - Instruction *InsertPt = &*F.getEntryBlock().begin(); - IRBuilder<> EntryIRB(InsertPt); + // Remove memory attributes that are about to become invalid. + // HWASan checks read from shadow, which invalidates memory(argmem: *) + // Short granule checks on function arguments read from the argument memory + // (last byte of the granule), which invalidates writeonly. + F.removeFnAttr(llvm::Attribute::Memory); + for (auto &A : F.args()) + A.removeAttr(llvm::Attribute::WriteOnly); + + BasicBlock::iterator InsertPt = F.getEntryBlock().begin(); + IRBuilder<> EntryIRB(&F.getEntryBlock(), InsertPt); emitPrologue(EntryIRB, /*WithFrameRecord*/ ClRecordStackHistory != none && Mapping.WithFrameRecord && @@ -1552,12 +1641,12 @@ void HWAddressSanitizer::sanitizeFunction(Function &F, // entry block back into the entry block so that they aren't treated as // dynamic allocas. if (EntryIRB.GetInsertBlock() != &F.getEntryBlock()) { - InsertPt = &*F.getEntryBlock().begin(); + InsertPt = F.getEntryBlock().begin(); for (Instruction &I : llvm::make_early_inc_range(*EntryIRB.GetInsertBlock())) { if (auto *AI = dyn_cast<AllocaInst>(&I)) if (isa<ConstantInt>(AI->getArraySize())) - I.moveBefore(InsertPt); + I.moveBefore(F.getEntryBlock(), InsertPt); } } @@ -1576,7 +1665,7 @@ void HWAddressSanitizer::sanitizeFunction(Function &F, ShadowBase = nullptr; StackBaseTag = nullptr; - CachedSP = nullptr; + CachedFP = nullptr; } void HWAddressSanitizer::instrumentGlobal(GlobalVariable *GV, uint8_t Tag) { |