Index: llvm/docs/BitCodeFormat.rst =================================================================== --- llvm/docs/BitCodeFormat.rst +++ llvm/docs/BitCodeFormat.rst @@ -1054,6 +1054,7 @@ * code 52: ``writeonly`` * code 53: ``speculatable`` * code 54: ``strictfp`` +* code 55: ``sanitize_hwaddress`` .. note:: The ``allocsize`` attribute has a special encoding for its arguments. Its two Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -1597,6 +1597,10 @@ ``sanitize_thread`` This attribute indicates that ThreadSanitizer checks (dynamic thread safety analysis) are enabled for this function. +``sanitize_hwaddress`` + This attribute indicates that HWAddressSanitizer checks + (dynamic address safety analysis based on tagged pointers) are enabled for + this function. ``speculatable`` This function attribute indicates that the function does not have any effects besides calculating its result and does not have undefined behavior. Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -560,6 +560,7 @@ ATTR_KIND_WRITEONLY = 52, ATTR_KIND_SPECULATABLE = 53, ATTR_KIND_STRICT_FP = 54, + ATTR_KIND_SANITIZE_HWADDRESS = 55, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -164,6 +164,9 @@ /// MemorySanitizer is on. def SanitizeMemory : EnumAttr<"sanitize_memory">; +/// HWAddressSanitizer is on. +def SanitizeHWAddress : EnumAttr<"sanitize_hwaddress">; + /// Argument is swift error. def SwiftError : EnumAttr<"swifterror">; @@ -200,6 +203,7 @@ def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; +def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; class MergeRule { Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -360,6 +360,7 @@ void initializeStripNonLineTableDebugInfoPass(PassRegistry&); void initializeStripSymbolsPass(PassRegistry&); void initializeStructurizeCFGPass(PassRegistry&); +void initializeHWAddressSanitizerPass(PassRegistry&); void initializeTailCallElimPass(PassRegistry&); void initializeTailDuplicatePassPass(PassRegistry&); void initializeTargetLibraryInfoWrapperPassPass(PassRegistry&); Index: llvm/include/llvm/Transforms/Instrumentation.h =================================================================== --- llvm/include/llvm/Transforms/Instrumentation.h +++ llvm/include/llvm/Transforms/Instrumentation.h @@ -133,6 +133,8 @@ FunctionPass *createMemorySanitizerPass(int TrackOrigins = 0, bool Recover = false); +FunctionPass *createHWAddressSanitizerPass(); + // Insert ThreadSanitizer (race detection) instrumentation FunctionPass *createThreadSanitizerPass(); Index: llvm/lib/Analysis/MemoryDependenceAnalysis.cpp =================================================================== --- llvm/lib/Analysis/MemoryDependenceAnalysis.cpp +++ llvm/lib/Analysis/MemoryDependenceAnalysis.cpp @@ -306,8 +306,10 @@ return 0; if (LIOffs + NewLoadByteSize > MemLocEnd && - LI->getParent()->getParent()->hasFnAttribute( - Attribute::SanitizeAddress)) + (LI->getParent()->getParent()->hasFnAttribute( + Attribute::SanitizeAddress) || + LI->getParent()->getParent()->hasFnAttribute( + Attribute::SanitizeHWAddress))) // We will be reading past the location accessed by the original program. // While this is safe in a regular build, Address Safety analysis tools // may start reporting false warnings. So, don't do widening. Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -3511,7 +3511,8 @@ // Speculative load may create a race that did not exist in the source. LI->getFunction()->hasFnAttribute(Attribute::SanitizeThread) || // Speculative load may load data from dirty regions. - LI->getFunction()->hasFnAttribute(Attribute::SanitizeAddress)) + LI->getFunction()->hasFnAttribute(Attribute::SanitizeAddress) || + LI->getFunction()->hasFnAttribute(Attribute::SanitizeHWAddress)) return false; const DataLayout &DL = LI->getModule()->getDataLayout(); return isDereferenceableAndAlignedPointer(LI->getPointerOperand(), Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -664,6 +664,7 @@ KEYWORD(strictfp); KEYWORD(safestack); KEYWORD(sanitize_address); + KEYWORD(sanitize_hwaddress); KEYWORD(sanitize_thread); KEYWORD(sanitize_memory); KEYWORD(swifterror); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -1144,6 +1144,8 @@ case lltok::kw_safestack: B.addAttribute(Attribute::SafeStack); break; case lltok::kw_sanitize_address: B.addAttribute(Attribute::SanitizeAddress); break; + case lltok::kw_sanitize_hwaddress: + B.addAttribute(Attribute::SanitizeHWAddress); break; case lltok::kw_sanitize_thread: B.addAttribute(Attribute::SanitizeThread); break; case lltok::kw_sanitize_memory: @@ -1468,6 +1470,7 @@ case lltok::kw_optsize: case lltok::kw_returns_twice: case lltok::kw_sanitize_address: + case lltok::kw_sanitize_hwaddress: case lltok::kw_sanitize_memory: case lltok::kw_sanitize_thread: case lltok::kw_ssp: @@ -1560,6 +1563,7 @@ case lltok::kw_optsize: case lltok::kw_returns_twice: case lltok::kw_sanitize_address: + case lltok::kw_sanitize_hwaddress: case lltok::kw_sanitize_memory: case lltok::kw_sanitize_thread: case lltok::kw_ssp: Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -172,6 +172,7 @@ kw_alwaysinline, kw_argmemonly, kw_sanitize_address, + kw_sanitize_hwaddress, kw_builtin, kw_byval, kw_inalloca, Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1156,6 +1156,7 @@ case Attribute::WriteOnly: return 1ULL << 53; case Attribute::Speculatable: return 1ULL << 54; case Attribute::StrictFP: return 1ULL << 55; + case Attribute::SanitizeHWAddress: return 1ULL << 56; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; @@ -1368,6 +1369,8 @@ return Attribute::StructRet; case bitc::ATTR_KIND_SANITIZE_ADDRESS: return Attribute::SanitizeAddress; + case bitc::ATTR_KIND_SANITIZE_HWADDRESS: + return Attribute::SanitizeHWAddress; case bitc::ATTR_KIND_SANITIZE_THREAD: return Attribute::SanitizeThread; case bitc::ATTR_KIND_SANITIZE_MEMORY: Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -663,6 +663,8 @@ return bitc::ATTR_KIND_STRUCT_RET; case Attribute::SanitizeAddress: return bitc::ATTR_KIND_SANITIZE_ADDRESS; + case Attribute::SanitizeHWAddress: + return bitc::ATTR_KIND_SANITIZE_HWADDRESS; case Attribute::SanitizeThread: return bitc::ATTR_KIND_SANITIZE_THREAD; case Attribute::SanitizeMemory: Index: llvm/lib/CodeGen/ShrinkWrap.cpp =================================================================== --- llvm/lib/CodeGen/ShrinkWrap.cpp +++ llvm/lib/CodeGen/ShrinkWrap.cpp @@ -558,16 +558,17 @@ switch (EnableShrinkWrapOpt) { case cl::BOU_UNSET: return TFI->enableShrinkWrapping(MF) && - // Windows with CFI has some limitations that make it impossible - // to use shrink-wrapping. - !MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && - // Sanitizers look at the value of the stack at the location - // of the crash. Since a crash can happen anywhere, the - // frame must be lowered before anything else happen for the - // sanitizers to be able to get a correct stack frame. - !(MF.getFunction()->hasFnAttribute(Attribute::SanitizeAddress) || - MF.getFunction()->hasFnAttribute(Attribute::SanitizeThread) || - MF.getFunction()->hasFnAttribute(Attribute::SanitizeMemory)); + // Windows with CFI has some limitations that make it impossible + // to use shrink-wrapping. + !MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + // Sanitizers look at the value of the stack at the location + // of the crash. Since a crash can happen anywhere, the + // frame must be lowered before anything else happen for the + // sanitizers to be able to get a correct stack frame. + !(MF.getFunction()->hasFnAttribute(Attribute::SanitizeAddress) || + MF.getFunction()->hasFnAttribute(Attribute::SanitizeThread) || + MF.getFunction()->hasFnAttribute(Attribute::SanitizeMemory) || + MF.getFunction()->hasFnAttribute(Attribute::SanitizeHWAddress)); // If EnableShrinkWrap is set, it takes precedence on whatever the // target sets. The rational is that we assume we want to test // something related to shrink-wrapping. Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -245,6 +245,8 @@ if (hasAttribute(Attribute::SanitizeAddress)) return "sanitize_address"; + if (hasAttribute(Attribute::SanitizeHWAddress)) + return "sanitize_hwaddress"; if (hasAttribute(Attribute::AlwaysInline)) return "alwaysinline"; if (hasAttribute(Attribute::ArgMemOnly)) Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1375,6 +1375,7 @@ case Attribute::NonLazyBind: case Attribute::ReturnsTwice: case Attribute::SanitizeAddress: + case Attribute::SanitizeHWAddress: case Attribute::SanitizeThread: case Attribute::SanitizeMemory: case Attribute::MinSize: Index: llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp =================================================================== --- llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -52,6 +52,7 @@ .Case("returns_twice", Attribute::ReturnsTwice) .Case("safestack", Attribute::SafeStack) .Case("sanitize_address", Attribute::SanitizeAddress) + .Case("sanitize_hwaddress", Attribute::SanitizeHWAddress) .Case("sanitize_memory", Attribute::SanitizeMemory) .Case("sanitize_thread", Attribute::SanitizeThread) .Case("ssp", Attribute::StackProtect) Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3607,7 +3607,8 @@ case Intrinsic::lifetime_start: // Asan needs to poison memory to detect invalid access which is possible // even for empty lifetime range. - if (II->getFunction()->hasFnAttribute(Attribute::SanitizeAddress)) + if (II->getFunction()->hasFnAttribute(Attribute::SanitizeAddress) || + II->getFunction()->hasFnAttribute(Attribute::SanitizeHWAddress)) break; if (removeTriviallyEmptyRange(*II, Intrinsic::lifetime_start, Index: llvm/lib/Transforms/Instrumentation/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ llvm/lib/Transforms/Instrumentation/CMakeLists.txt @@ -12,6 +12,7 @@ SanitizerCoverage.cpp ThreadSanitizer.cpp EfficiencySanitizer.cpp + HWAddressSanitizer.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms Index: llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -0,0 +1,341 @@ +//===- HWAddressSanitizer.cpp - detector of uninitialized reads -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file is a part of HWAddressSanitizer, an address sanity checker +/// based on tagged addressing. +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/IR/ValueMap.h" +#include "llvm/Pass.h" +#include "llvm/Support/AtomicOrdering.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "hwasan" + +static const char *const kHwasanModuleCtorName = "hwasan.module_ctor"; +static const char *const kHwasanInitName = "__hwasan_init"; + +// Accesses sizes are powers of two: 1, 2, 4, 8, 16. +static const size_t kNumberOfAccessSizes = 5; + +static cl::opt ClMemoryAccessCallbackPrefix( + "hwasan-memory-access-callback-prefix", + cl::desc("Prefix for memory access callbacks"), cl::Hidden, + cl::init("__hwasan_")); + +static cl::opt ClInstrumentReads("hwasan-instrument-reads", + cl::desc("instrument read instructions"), + cl::Hidden, cl::init(true)); + +static cl::opt ClInstrumentWrites( + "hwasan-instrument-writes", cl::desc("instrument write instructions"), + cl::Hidden, cl::init(true)); + +static cl::opt ClInstrumentAtomics( + "hwasan-instrument-atomics", + cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden, + cl::init(true)); + +namespace { + +/// \brief An instrumentation pass implementing detection of addressability bugs +/// using tagged pointers. +class HWAddressSanitizer : public FunctionPass { +public: + // Pass identification, replacement for typeid. + static char ID; + + HWAddressSanitizer() : FunctionPass(ID) {} + + StringRef getPassName() const override { return "HWAddressSanitizer"; } + + bool runOnFunction(Function &F) override; + bool doInitialization(Module &M) override; + + void initializeCallbacks(Module &M); + bool instrumentMemAccess(Instruction *I); + Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite, + uint64_t *TypeSize, unsigned *Alignment, + Value **MaybeMask); + +private: + LLVMContext *C; + Type *IntptrTy; + + Function *HwasanCtorFunction; + + Function *HwasanMemoryAccessCallback[2][kNumberOfAccessSizes]; + Function *HwasanMemoryAccessCallbackSized[2]; +}; + +} // end anonymous namespace + +char HWAddressSanitizer::ID = 0; + +INITIALIZE_PASS_BEGIN( + HWAddressSanitizer, "hwasan", + "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END( + HWAddressSanitizer, "hwasan", + "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) + +FunctionPass *llvm::createHWAddressSanitizerPass() { + return new HWAddressSanitizer(); +} + +/// \brief Module-level initialization. +/// +/// inserts a call to __hwasan_init to the module's constructor list. +bool HWAddressSanitizer::doInitialization(Module &M) { + DEBUG(dbgs() << "Init " << M.getName() << "\n"); + auto &DL = M.getDataLayout(); + + Triple TargetTriple(M.getTargetTriple()); + + C = &(M.getContext()); + IRBuilder<> IRB(*C); + IntptrTy = IRB.getIntPtrTy(DL); + + std::tie(HwasanCtorFunction, std::ignore) = + createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName, + kHwasanInitName, + /*InitArgTypes=*/{}, + /*InitArgs=*/{}); + appendToGlobalCtors(M, HwasanCtorFunction, 0); + return true; +} + +void HWAddressSanitizer::initializeCallbacks(Module &M) { + IRBuilder<> IRB(*C); + for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) { + const std::string TypeStr = AccessIsWrite ? "store" : "load"; + + HwasanMemoryAccessCallbackSized[AccessIsWrite] = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + ClMemoryAccessCallbackPrefix + TypeStr, + FunctionType::get(IRB.getVoidTy(), {IntptrTy, IntptrTy}, false))); + + for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes; + AccessSizeIndex++) { + HwasanMemoryAccessCallback[AccessIsWrite][AccessSizeIndex] = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + ClMemoryAccessCallbackPrefix + TypeStr + + itostr(1ULL << AccessSizeIndex), + FunctionType::get(IRB.getVoidTy(), {IntptrTy}, false))); + } + } +} + +Value *HWAddressSanitizer::isInterestingMemoryAccess(Instruction *I, + bool *IsWrite, + uint64_t *TypeSize, + unsigned *Alignment, + Value **MaybeMask) { + // Skip memory accesses inserted by another instrumentation. + if (I->getMetadata("nosanitize")) return nullptr; + + Value *PtrOperand = nullptr; + const DataLayout &DL = I->getModule()->getDataLayout(); + if (LoadInst *LI = dyn_cast(I)) { + if (!ClInstrumentReads) return nullptr; + *IsWrite = false; + *TypeSize = DL.getTypeStoreSizeInBits(LI->getType()); + *Alignment = LI->getAlignment(); + PtrOperand = LI->getPointerOperand(); + } else if (StoreInst *SI = dyn_cast(I)) { + if (!ClInstrumentWrites) return nullptr; + *IsWrite = true; + *TypeSize = DL.getTypeStoreSizeInBits(SI->getValueOperand()->getType()); + *Alignment = SI->getAlignment(); + PtrOperand = SI->getPointerOperand(); + } else if (AtomicRMWInst *RMW = dyn_cast(I)) { + if (!ClInstrumentAtomics) return nullptr; + *IsWrite = true; + *TypeSize = DL.getTypeStoreSizeInBits(RMW->getValOperand()->getType()); + *Alignment = 0; + PtrOperand = RMW->getPointerOperand(); + } else if (AtomicCmpXchgInst *XCHG = dyn_cast(I)) { + if (!ClInstrumentAtomics) return nullptr; + *IsWrite = true; + *TypeSize = DL.getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType()); + *Alignment = 0; + PtrOperand = XCHG->getPointerOperand(); + } else if (auto CI = dyn_cast(I)) { + auto *F = dyn_cast(CI->getCalledValue()); + if (F && (F->getName().startswith("llvm.masked.load.") || + F->getName().startswith("llvm.masked.store."))) { + unsigned OpOffset = 0; + if (F->getName().startswith("llvm.masked.store.")) { + if (!ClInstrumentWrites) + return nullptr; + // Masked store has an initial operand for the value. + OpOffset = 1; + *IsWrite = true; + } else { + if (!ClInstrumentReads) + return nullptr; + *IsWrite = false; + } + + auto BasePtr = CI->getOperand(0 + OpOffset); + auto Ty = cast(BasePtr->getType())->getElementType(); + *TypeSize = DL.getTypeStoreSizeInBits(Ty); + if (auto AlignmentConstant = + dyn_cast(CI->getOperand(1 + OpOffset))) + *Alignment = (unsigned)AlignmentConstant->getZExtValue(); + else + *Alignment = 1; // No alignment guarantees. We probably got Undef + if (MaybeMask) + *MaybeMask = CI->getOperand(2 + OpOffset); + PtrOperand = BasePtr; + } + } + + if (PtrOperand) { + // Do not instrument acesses from different address spaces; we cannot deal + // with them. + Type *PtrTy = cast(PtrOperand->getType()->getScalarType()); + if (PtrTy->getPointerAddressSpace() != 0) + return nullptr; + + // Ignore swifterror addresses. + // swifterror memory addresses are mem2reg promoted by instruction + // selection. As such they cannot have regular uses like an instrumentation + // function and it makes no sense to track them as memory. + if (PtrOperand->isSwiftError()) + return nullptr; + } + + return PtrOperand; +} + +static size_t TypeSizeToSizeIndex(uint32_t TypeSize) { + size_t Res = countTrailingZeros(TypeSize / 8); + assert(Res < kNumberOfAccessSizes); + return Res; +} + +bool HWAddressSanitizer::instrumentMemAccess(Instruction *I) { + DEBUG(dbgs() << "Instrumenting: " << *I << "\n"); + bool IsWrite = false; + unsigned Alignment = 0; + uint64_t TypeSize = 0; + Value *MaybeMask = nullptr; + Value *Addr = + isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment, &MaybeMask); + + if (!Addr) + return false; + + if (MaybeMask) + return false; //FIXME + + IRBuilder<> IRB(I); + Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy); + if (isPowerOf2_64(TypeSize) && + (TypeSize / 8 <= (1UL << (kNumberOfAccessSizes - 1)))) { + size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize); + IRB.CreateCall(HwasanMemoryAccessCallback[IsWrite][AccessSizeIndex], + AddrLong); + } else { + IRB.CreateCall(HwasanMemoryAccessCallbackSized[IsWrite], + {AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8)}); + } + + return true; +} + +bool HWAddressSanitizer::runOnFunction(Function &F) { + if (&F == HwasanCtorFunction) + return false; + + if (!F.hasFnAttribute(Attribute::SanitizeHWAddress)) + return false; + + DEBUG(dbgs() << "Function: " << F.getName() << "\n"); + + initializeCallbacks(*F.getParent()); + + bool Changed = false; + SmallVector ToInstrument; + for (auto &BB : F) { + for (auto &Inst : BB) { + Value *MaybeMask = nullptr; + bool IsWrite; + unsigned Alignment; + uint64_t TypeSize; + Value *Addr = isInterestingMemoryAccess(&Inst, &IsWrite, &TypeSize, + &Alignment, &MaybeMask); + if (Addr || isa(Inst)) + ToInstrument.push_back(&Inst); + } + } + + for (auto Inst : ToInstrument) { + Changed |= instrumentMemAccess(Inst); + } + + return Changed; +} Index: llvm/lib/Transforms/Instrumentation/Instrumentation.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/Instrumentation.cpp +++ llvm/lib/Transforms/Instrumentation/Instrumentation.cpp @@ -66,6 +66,7 @@ initializePGOMemOPSizeOptLegacyPassPass(Registry); initializeInstrProfilingLegacyPassPass(Registry); initializeMemorySanitizerPass(Registry); + initializeHWAddressSanitizerPass(Registry); initializeThreadSanitizerPass(Registry); initializeSanitizerCoverageModulePass(Registry); initializeDataFlowSanitizerPass(Registry); Index: llvm/lib/Transforms/Scalar/GVN.cpp =================================================================== --- llvm/lib/Transforms/Scalar/GVN.cpp +++ llvm/lib/Transforms/Scalar/GVN.cpp @@ -1299,7 +1299,10 @@ /// non-local by performing PHI construction. bool GVN::processNonLocalLoad(LoadInst *LI) { // non-local speculations are not allowed under asan. - if (LI->getParent()->getParent()->hasFnAttribute(Attribute::SanitizeAddress)) + if (LI->getParent()->getParent()->hasFnAttribute( + Attribute::SanitizeAddress) || + LI->getParent()->getParent()->hasFnAttribute( + Attribute::SanitizeHWAddress)) return false; // Step 1: Find the non-local dependencies of the load. Index: llvm/test/Bitcode/attributes.ll =================================================================== --- llvm/test/Bitcode/attributes.ll +++ llvm/test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #34 +; CHECK: call void @nobuiltin() #35 ret void; } @@ -339,6 +339,12 @@ ret void } +; CHECK: define void @f58() #34 +define void @f58() sanitize_hwaddress +{ + ret void; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -373,4 +379,5 @@ ; CHECK: attributes #31 = { allocsize(0,1) } ; CHECK: attributes #32 = { writeonly } ; CHECK: attributes #33 = { speculatable } -; CHECK: attributes #34 = { nobuiltin } +; CHECK: attributes #34 = { sanitize_hwaddress } +; CHECK: attributes #35 = { nobuiltin } Index: llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll @@ -0,0 +1,26 @@ +; Test basic address sanitizer instrumentation. +; +; RUN: opt < %s -hwasan -S | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64--linux-android" + +define void @atomicrmw(i64* %ptr) sanitize_hwaddress { +; CHECK-LABEL: @atomicrmw( +; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64 +; CHECK: call void @__hwasan_store8(i64 %[[A]]) + +entry: + %0 = atomicrmw add i64* %ptr, i64 1 seq_cst + ret void +} + +define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddress { +; CHECK-LABEL: @cmpxchg( +; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64 +; CHECK: call void @__hwasan_store8(i64 %[[A]]) + +entry: + %0 = cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst + ret void +} Index: llvm/test/Instrumentation/HWAddressSanitizer/basic.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/HWAddressSanitizer/basic.ll @@ -0,0 +1,179 @@ +; Test basic address sanitizer instrumentation. +; +; RUN: opt < %s -hwasan -S | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64--linux-android" + +define i8 @test_load8(i8* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load8( +; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64 +; CHECK: call void @__hwasan_load1(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i16 @test_load16(i16* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load16( +; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64 +; CHECK: call void @__hwasan_load2(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i16, i16* %a +; CHECK: ret i16 %[[B]] + +entry: + %b = load i16, i16* %a, align 4 + ret i16 %b +} + +define i32 @test_load32(i32* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load32( +; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64 +; CHECK: call void @__hwasan_load4(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i32, i32* %a +; CHECK: ret i32 %[[B]] + +entry: + %b = load i32, i32* %a, align 4 + ret i32 %b +} + +define i64 @test_load64(i64* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load64( +; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64 +; CHECK: call void @__hwasan_load8(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i64, i64* %a +; CHECK: ret i64 %[[B]] + +entry: + %b = load i64, i64* %a, align 8 + ret i64 %b +} + +define i128 @test_load128(i128* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load128( +; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64 +; CHECK: call void @__hwasan_load16(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i128, i128* %a +; CHECK: ret i128 %[[B]] + +entry: + %b = load i128, i128* %a, align 16 + ret i128 %b +} + +define i40 @test_load40(i40* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load40( +; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64 +; CHECK: call void @__hwasan_load(i64 %[[A]], i64 5) +; CHECK: %[[B:[^ ]*]] = load i40, i40* %a +; CHECK: ret i40 %[[B]] + +entry: + %b = load i40, i40* %a, align 4 + ret i40 %b +} + +define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store8( +; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64 +; CHECK: call void @__hwasan_store1(i64 %[[A]]) +; CHECK: store i8 %b, i8* %a +; CHECK: ret void + +entry: + store i8 %b, i8* %a, align 4 + ret void +} + +define void @test_store16(i16* %a, i16 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store16( +; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64 +; CHECK: call void @__hwasan_store2(i64 %[[A]]) +; CHECK: store i16 %b, i16* %a +; CHECK: ret void + +entry: + store i16 %b, i16* %a, align 4 + ret void +} + +define void @test_store32(i32* %a, i32 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store32( +; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64 +; CHECK: call void @__hwasan_store4(i64 %[[A]]) +; CHECK: store i32 %b, i32* %a +; CHECK: ret void + +entry: + store i32 %b, i32* %a, align 4 + ret void +} + +define void @test_store64(i64* %a, i64 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store64( +; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64 +; CHECK: call void @__hwasan_store8(i64 %[[A]]) +; CHECK: store i64 %b, i64* %a +; CHECK: ret void + +entry: + store i64 %b, i64* %a, align 4 + ret void +} + +define void @test_store128(i128* %a, i128 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store128( +; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64 +; CHECK: call void @__hwasan_store16(i64 %[[A]]) +; CHECK: store i128 %b, i128* %a +; CHECK: ret void + +entry: + store i128 %b, i128* %a, align 4 + ret void +} + +define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store40( +; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64 +; CHECK: call void @__hwasan_store(i64 %[[A]], i64 5) +; CHECK: store i40 %b, i40* %a +; CHECK: ret void + +entry: + store i40 %b, i40* %a, align 4 + ret void +} + +define i8 @test_load_noattr(i8* %a) { +; CHECK-LABEL: @test_load_noattr( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i8 @test_load_notmyattr(i8* %a) sanitize_address { +; CHECK-LABEL: @test_load_notmyattr( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +; CHECK: declare void @__hwasan_init() + +; CHECK: define internal void @hwasan.module_ctor() { +; CHECK-NEXT: call void @__hwasan_init() +; CHECK-NEXT: ret void +; CHECK-NEXT: } Index: llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll =================================================================== --- llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll +++ llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll @@ -53,3 +53,30 @@ ; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32 ; CHECK: {{.*}} = phi + +define i32 @TestHWAsan() sanitize_hwaddress { + %1 = tail call noalias i8* @_Znam(i64 2) + %2 = getelementptr inbounds i8, i8* %1, i64 1 + store i8 0, i8* %2, align 1 + store i8 0, i8* %1, align 1 + %3 = bitcast i8* %1 to i16* + %4 = load i16, i16* %3, align 4 + %5 = icmp eq i16 %4, 0 + br i1 %5, label %11, label %6 + +;