diff options
Diffstat (limited to 'llvm/lib/Transforms')
| -rw-r--r-- | llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp | 229 | 
1 files changed, 219 insertions, 10 deletions
| diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp index 9633f5a5f8d..f09bf25deaf 100644 --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -16,6 +16,7 @@  #include "llvm/ADT/StringExtras.h"  #include "llvm/ADT/StringRef.h"  #include "llvm/ADT/Triple.h" +#include "llvm/BinaryFormat/ELF.h"  #include "llvm/IR/Attributes.h"  #include "llvm/IR/BasicBlock.h"  #include "llvm/IR/Constant.h" @@ -52,6 +53,7 @@ using namespace llvm;  #define DEBUG_TYPE "hwasan"  static const char *const kHwasanModuleCtorName = "hwasan.module_ctor"; +static const char *const kHwasanNoteName = "hwasan.note";  static const char *const kHwasanInitName = "__hwasan_init";  static const char *const kHwasanShadowMemoryDynamicAddress = @@ -112,6 +114,9 @@ static cl::opt<bool> ClGenerateTagsWithCalls(      cl::desc("generate new tags with runtime library calls"), cl::Hidden,      cl::init(false)); +static cl::opt<bool> ClGlobals("hwasan-globals", cl::desc("Instrument globals"), +                               cl::Hidden, cl::init(false)); +  static cl::opt<int> ClMatchAllTag(      "hwasan-match-all-tag",      cl::desc("don't report bad accesses via pointers with this tag"), @@ -169,16 +174,16 @@ namespace {  class HWAddressSanitizer {  public:    explicit HWAddressSanitizer(Module &M, bool CompileKernel = false, -                              bool Recover = false) { +                              bool Recover = false) : M(M) {      this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover;      this->CompileKernel = ClEnableKhwasan.getNumOccurrences() > 0 ?          ClEnableKhwasan : CompileKernel; -    initializeModule(M); +    initializeModule();    }    bool sanitizeFunction(Function &F); -  void initializeModule(Module &M); +  void initializeModule();    void initializeCallbacks(Module &M); @@ -216,8 +221,12 @@ public:    Value *getHwasanThreadSlotPtr(IRBuilder<> &IRB, Type *Ty);    void emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord); +  void instrumentGlobal(GlobalVariable *GV, uint8_t Tag); +  void instrumentGlobals(); +  private:    LLVMContext *C; +  Module &M;    Triple TargetTriple;    FunctionCallee HWAsanMemmove, HWAsanMemcpy, HWAsanMemset;    FunctionCallee HWAsanHandleVfork; @@ -237,7 +246,7 @@ private:      bool InTls;      void init(Triple &TargetTriple); -    unsigned getAllocaAlignment() const { return 1U << Scale; } +    unsigned getObjectAlignment() const { return 1U << Scale; }    };    ShadowMapping Mapping; @@ -245,6 +254,7 @@ private:    Type *Int8PtrTy;    Type *Int8Ty;    Type *Int32Ty; +  Type *Int64Ty = Type::getInt64Ty(M.getContext());    bool CompileKernel;    bool Recover; @@ -332,7 +342,7 @@ PreservedAnalyses HWAddressSanitizerPass::run(Module &M,  /// Module-level initialization.  ///  /// inserts a call to __hwasan_init to the module's constructor list. -void HWAddressSanitizer::initializeModule(Module &M) { +void HWAddressSanitizer::initializeModule() {    LLVM_DEBUG(dbgs() << "Init " << M.getName() << "\n");    auto &DL = M.getDataLayout(); @@ -361,6 +371,16 @@ void HWAddressSanitizer::initializeModule(Module &M) {                Ctor->setComdat(CtorComdat);                appendToGlobalCtors(M, Ctor, 0, Ctor);              }); + +    // Older versions of Android do not have the required runtime support for +    // global instrumentation. On other platforms we currently require using the +    // latest version of the runtime. +    bool InstrumentGlobals = +        !TargetTriple.isAndroid() || !TargetTriple.isAndroidVersionLT(30); +    if (ClGlobals.getNumOccurrences()) +      InstrumentGlobals = ClGlobals; +    if (InstrumentGlobals) +      instrumentGlobals();    }    if (!TargetTriple.isAndroid()) { @@ -716,7 +736,7 @@ static uint64_t getAllocaSizeInBytes(const AllocaInst &AI) {  bool HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI,                                     Value *Tag, size_t Size) { -  size_t AlignedSize = alignTo(Size, Mapping.getAllocaAlignment()); +  size_t AlignedSize = alignTo(Size, Mapping.getObjectAlignment());    Value *JustTag = IRB.CreateTrunc(Tag, IRB.getInt8Ty());    if (ClInstrumentWithCalls) { @@ -736,7 +756,7 @@ bool HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI,        IRB.CreateMemSet(ShadowPtr, JustTag, ShadowSize, /*Align=*/1);      if (Size != AlignedSize) {        IRB.CreateStore( -          ConstantInt::get(Int8Ty, Size % Mapping.getAllocaAlignment()), +          ConstantInt::get(Int8Ty, Size % Mapping.getObjectAlignment()),            IRB.CreateConstGEP1_32(Int8Ty, ShadowPtr, ShadowSize));        IRB.CreateStore(JustTag, IRB.CreateConstGEP1_32(                                     Int8Ty, IRB.CreateBitCast(AI, Int8PtrTy), @@ -1018,7 +1038,7 @@ bool HWAddressSanitizer::instrumentStack(        // Re-tag alloca memory with the special UAR tag.        Value *Tag = getUARTag(IRB, StackTag); -      tagAlloca(IRB, AI, Tag, alignTo(Size, Mapping.getAllocaAlignment())); +      tagAlloca(IRB, AI, Tag, alignTo(Size, Mapping.getObjectAlignment()));      }    } @@ -1116,8 +1136,9 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {    DenseMap<AllocaInst *, AllocaInst *> AllocaToPaddedAllocaMap;    for (AllocaInst *AI : AllocasToInstrument) {      uint64_t Size = getAllocaSizeInBytes(*AI); -    uint64_t AlignedSize = alignTo(Size, Mapping.getAllocaAlignment()); -    AI->setAlignment(std::max(AI->getAlignment(), 16u)); +    uint64_t AlignedSize = alignTo(Size, Mapping.getObjectAlignment()); +    AI->setAlignment( +        std::max(AI->getAlignment(), Mapping.getObjectAlignment()));      if (Size != AlignedSize) {        Type *AllocatedType = AI->getAllocatedType();        if (AI->isArrayAllocation()) { @@ -1177,6 +1198,194 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {    return Changed;  } +void HWAddressSanitizer::instrumentGlobal(GlobalVariable *GV, uint8_t Tag) { +  Constant *Initializer = GV->getInitializer(); +  uint64_t SizeInBytes = +      M.getDataLayout().getTypeAllocSize(Initializer->getType()); +  uint64_t NewSize = alignTo(SizeInBytes, Mapping.getObjectAlignment()); +  if (SizeInBytes != NewSize) { +    // Pad the initializer out to the next multiple of 16 bytes and add the +    // required short granule tag. +    std::vector<uint8_t> Init(NewSize - SizeInBytes, 0); +    Init.back() = Tag; +    Constant *Padding = ConstantDataArray::get(*C, Init); +    Initializer = ConstantStruct::getAnon({Initializer, Padding}); +  } + +  auto *NewGV = new GlobalVariable(M, Initializer->getType(), GV->isConstant(), +                                   GlobalValue::ExternalLinkage, Initializer, +                                   GV->getName() + ".hwasan"); +  NewGV->copyAttributesFrom(GV); +  NewGV->setLinkage(GlobalValue::PrivateLinkage); +  NewGV->copyMetadata(GV, 0); +  NewGV->setAlignment( +      std::max(GV->getAlignment(), Mapping.getObjectAlignment())); + +  // It is invalid to ICF two globals that have different tags. In the case +  // where the size of the global is a multiple of the tag granularity the +  // contents of the globals may be the same but the tags (i.e. symbol values) +  // may be different, and the symbols are not considered during ICF. In the +  // case where the size is not a multiple of the granularity, the short granule +  // tags would discriminate two globals with different tags, but there would +  // otherwise be nothing stopping such a global from being incorrectly ICF'd +  // with an uninstrumented (i.e. tag 0) global that happened to have the short +  // granule tag in the last byte. +  NewGV->setUnnamedAddr(GlobalValue::UnnamedAddr::None); + +  // Descriptor format (assuming little-endian): +  // bytes 0-3: relative address of global +  // bytes 4-6: size of global (16MB ought to be enough for anyone, but in case +  // it isn't, we create multiple descriptors) +  // byte 7: tag +  auto *DescriptorTy = StructType::get(Int32Ty, Int32Ty); +  const uint64_t MaxDescriptorSize = 0xfffff0; +  for (uint64_t DescriptorPos = 0; DescriptorPos < SizeInBytes; +       DescriptorPos += MaxDescriptorSize) { +    auto *Descriptor = +        new GlobalVariable(M, DescriptorTy, true, GlobalValue::PrivateLinkage, +                           nullptr, GV->getName() + ".hwasan.descriptor"); +    auto *GVRelPtr = ConstantExpr::getTrunc( +        ConstantExpr::getAdd( +            ConstantExpr::getSub( +                ConstantExpr::getPtrToInt(NewGV, Int64Ty), +                ConstantExpr::getPtrToInt(Descriptor, Int64Ty)), +            ConstantInt::get(Int64Ty, DescriptorPos)), +        Int32Ty); +    uint32_t Size = std::min(SizeInBytes - DescriptorPos, MaxDescriptorSize); +    auto *SizeAndTag = ConstantInt::get(Int32Ty, Size | (uint32_t(Tag) << 24)); +    Descriptor->setComdat(NewGV->getComdat()); +    Descriptor->setInitializer(ConstantStruct::getAnon({GVRelPtr, SizeAndTag})); +    Descriptor->setSection("hwasan_globals"); +    Descriptor->setMetadata(LLVMContext::MD_associated, +                            MDNode::get(*C, ValueAsMetadata::get(NewGV))); +    appendToCompilerUsed(M, Descriptor); +  } + +  Constant *Aliasee = ConstantExpr::getIntToPtr( +      ConstantExpr::getAdd( +          ConstantExpr::getPtrToInt(NewGV, Int64Ty), +          ConstantInt::get(Int64Ty, uint64_t(Tag) << kPointerTagShift)), +      GV->getType()); +  auto *Alias = GlobalAlias::create(GV->getValueType(), GV->getAddressSpace(), +                                    GV->getLinkage(), "", Aliasee, &M); +  Alias->setVisibility(GV->getVisibility()); +  Alias->takeName(GV); +  GV->replaceAllUsesWith(Alias); +  GV->eraseFromParent(); +} + +void HWAddressSanitizer::instrumentGlobals() { +  // Start by creating a note that contains pointers to the list of global +  // descriptors. Adding a note to the output file will cause the linker to +  // create a PT_NOTE program header pointing to the note that we can use to +  // find the descriptor list starting from the program headers. A function +  // provided by the runtime initializes the shadow memory for the globals by +  // accessing the descriptor list via the note. The dynamic loader needs to +  // call this function whenever a library is loaded. +  // +  // The reason why we use a note for this instead of a more conventional +  // approach of having a global constructor pass a descriptor list pointer to +  // the runtime is because of an order of initialization problem. With +  // constructors we can encounter the following problematic scenario: +  // +  // 1) library A depends on library B and also interposes one of B's symbols +  // 2) B's constructors are called before A's (as required for correctness) +  // 3) during construction, B accesses one of its "own" globals (actually +  //    interposed by A) and triggers a HWASAN failure due to the initialization +  //    for A not having happened yet +  // +  // Even without interposition it is possible to run into similar situations in +  // cases where two libraries mutually depend on each other. +  // +  // We only need one note per binary, so put everything for the note in a +  // comdat. +  Comdat *NoteComdat = M.getOrInsertComdat(kHwasanNoteName); + +  Type *Int8Arr0Ty = ArrayType::get(Int8Ty, 0); +  auto Start = +      new GlobalVariable(M, Int8Arr0Ty, true, GlobalVariable::ExternalLinkage, +                         nullptr, "__start_hwasan_globals"); +  Start->setVisibility(GlobalValue::HiddenVisibility); +  Start->setDSOLocal(true); +  auto Stop = +      new GlobalVariable(M, Int8Arr0Ty, true, GlobalVariable::ExternalLinkage, +                         nullptr, "__stop_hwasan_globals"); +  Stop->setVisibility(GlobalValue::HiddenVisibility); +  Stop->setDSOLocal(true); + +  // Null-terminated so actually 8 bytes, which are required in order to align +  // the note properly. +  auto *Name = ConstantDataArray::get(*C, "LLVM\0\0\0"); + +  auto *NoteTy = StructType::get(Int32Ty, Int32Ty, Int32Ty, Name->getType(), +                                 Int32Ty, Int32Ty); +  auto *Note = +      new GlobalVariable(M, NoteTy, /*isConstantGlobal=*/true, +                         GlobalValue::PrivateLinkage, nullptr, kHwasanNoteName); +  Note->setSection(".note.hwasan.globals"); +  Note->setComdat(NoteComdat); +  Note->setAlignment(4); +  Note->setDSOLocal(true); + +  // The pointers in the note need to be relative so that the note ends up being +  // placed in rodata, which is the standard location for notes. +  auto CreateRelPtr = [&](Constant *Ptr) { +    return ConstantExpr::getTrunc( +        ConstantExpr::getSub(ConstantExpr::getPtrToInt(Ptr, Int64Ty), +                             ConstantExpr::getPtrToInt(Note, Int64Ty)), +        Int32Ty); +  }; +  Note->setInitializer(ConstantStruct::getAnon( +      {ConstantInt::get(Int32Ty, 8),                           // n_namesz +       ConstantInt::get(Int32Ty, 8),                           // n_descsz +       ConstantInt::get(Int32Ty, ELF::NT_LLVM_HWASAN_GLOBALS), // n_type +       Name, CreateRelPtr(Start), CreateRelPtr(Stop)})); +  appendToCompilerUsed(M, Note); + +  // Create a zero-length global in hwasan_globals so that the linker will +  // always create start and stop symbols. +  auto Dummy = new GlobalVariable( +      M, Int8Arr0Ty, /*isConstantGlobal*/ true, GlobalVariable::PrivateLinkage, +      Constant::getNullValue(Int8Arr0Ty), "hwasan.dummy.global"); +  Dummy->setSection("hwasan_globals"); +  Dummy->setComdat(NoteComdat); +  Dummy->setMetadata(LLVMContext::MD_associated, +                     MDNode::get(*C, ValueAsMetadata::get(Note))); +  appendToCompilerUsed(M, Dummy); + +  std::vector<GlobalVariable *> Globals; +  for (GlobalVariable &GV : M.globals()) { +    if (GV.isDeclarationForLinker() || GV.getName().startswith("llvm.") || +        GV.isThreadLocal()) +      continue; + +    // Common symbols can't have aliases point to them, so they can't be tagged. +    if (GV.hasCommonLinkage()) +      continue; + +    // Globals with custom sections may be used in __start_/__stop_ enumeration, +    // which would be broken both by adding tags and potentially by the extra +    // padding/alignment that we insert. +    if (GV.hasSection()) +      continue; + +    Globals.push_back(&GV); +  } + +  MD5 Hasher; +  Hasher.update(M.getSourceFileName()); +  MD5::MD5Result Hash; +  Hasher.final(Hash); +  uint8_t Tag = Hash[0]; + +  for (GlobalVariable *GV : Globals) { +    // Skip tag 0 in order to avoid collisions with untagged memory. +    if (Tag == 0) +      Tag = 1; +    instrumentGlobal(GV, Tag++); +  } +} +  void HWAddressSanitizer::ShadowMapping::init(Triple &TargetTriple) {    Scale = kDefaultShadowScale;    if (ClMappingOffset.getNumOccurrences() > 0) { | 

