This revision was automatically updated to reflect the committed changes.
Closed by commit rL368102: hwasan: Instrument globals. (authored by pcc, 
committed by ).
Herald added a subscriber: delcypher.

Changed prior to commit:
  https://reviews.llvm.org/D65770?vs=213477&id=213732#toc

Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D65770/new/

https://reviews.llvm.org/D65770

Files:
  cfe/trunk/lib/Driver/SanitizerArgs.cpp
  cfe/trunk/test/Driver/fsanitize.c
  compiler-rt/trunk/lib/hwasan/hwasan.cpp
  compiler-rt/trunk/lib/hwasan/hwasan.h
  compiler-rt/trunk/lib/hwasan/hwasan_interface_internal.h
  compiler-rt/trunk/lib/hwasan/hwasan_report.cpp
  compiler-rt/trunk/test/hwasan/TestCases/global.c
  compiler-rt/trunk/test/hwasan/lit.cfg.py
  llvm/trunk/include/llvm/BinaryFormat/ELF.h
  llvm/trunk/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
  llvm/trunk/test/Instrumentation/HWAddressSanitizer/globals.ll

Index: llvm/trunk/test/Instrumentation/HWAddressSanitizer/globals.ll
===================================================================
--- llvm/trunk/test/Instrumentation/HWAddressSanitizer/globals.ll
+++ llvm/trunk/test/Instrumentation/HWAddressSanitizer/globals.ll
@@ -0,0 +1,37 @@
+; RUN: opt < %s -S -hwasan -mtriple=aarch64--linux-android29 | FileCheck --check-prefix=CHECK29 %s
+; RUN: opt < %s -S -hwasan -mtriple=aarch64--linux-android30 | FileCheck --check-prefix=CHECK30 %s
+
+; CHECK29-NOT: @hwasan.note
+; CHECK29: @four = global
+
+; CHECK30: @__start_hwasan_globals = external hidden constant [0 x i8]
+; CHECK30: @__stop_hwasan_globals = external hidden constant [0 x i8]
+
+; CHECK30: @hwasan.note = private constant { i32, i32, i32, [8 x i8], i32, i32 } { i32 8, i32 8, i32 3, [8 x i8] c"LLVM\00\00\00\00", i32 trunc (i64 sub (i64 ptrtoint ([0 x i8]* @__start_hwasan_globals to i64), i64 ptrtoint ({ i32, i32, i32, [8 x i8], i32, i32 }* @hwasan.note to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([0 x i8]* @__stop_hwasan_globals to i64), i64 ptrtoint ({ i32, i32, i32, [8 x i8], i32, i32 }* @hwasan.note to i64)) to i32) }, section ".note.hwasan.globals", comdat, align 4
+
+; CHECK30: @hwasan.dummy.global = private constant [0 x i8] zeroinitializer, section "hwasan_globals", comdat($hwasan.note), !associated [[NOTE:![0-9]+]]
+
+; CHECK30: @four.hwasan = private global { i32, [12 x i8] } { i32 1, [12 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\AC" }, align 16
+; CHECK30: @four.hwasan.descriptor = private constant { i32, i32 } { i32 trunc (i64 sub (i64 ptrtoint ({ i32, [12 x i8] }* @four.hwasan to i64), i64 ptrtoint ({ i32, i32 }* @four.hwasan.descriptor to i64)) to i32), i32 -1409286140 }, section "hwasan_globals", !associated [[FOUR:![0-9]+]]
+
+; CHECK30: @sixteen.hwasan = private global [16 x i8] zeroinitializer, align 16
+; CHECK30: @sixteen.hwasan.descriptor = private constant { i32, i32 } { i32 trunc (i64 sub (i64 ptrtoint ([16 x i8]* @sixteen.hwasan to i64), i64 ptrtoint ({ i32, i32 }* @sixteen.hwasan.descriptor to i64)) to i32), i32 -1392508912 }, section "hwasan_globals", !associated [[SIXTEEN:![0-9]+]]
+
+; CHECK30: @huge.hwasan = private global [16777232 x i8] zeroinitializer, align 16
+; CHECK30: @huge.hwasan.descriptor = private constant { i32, i32 } { i32 trunc (i64 sub (i64 ptrtoint ([16777232 x i8]* @huge.hwasan to i64), i64 ptrtoint ({ i32, i32 }* @huge.hwasan.descriptor to i64)) to i32), i32 -1358954512 }, section "hwasan_globals", !associated [[HUGE:![0-9]+]]
+; CHECK30: @huge.hwasan.descriptor.1 = private constant { i32, i32 } { i32 trunc (i64 add (i64 sub (i64 ptrtoint ([16777232 x i8]* @huge.hwasan to i64), i64 ptrtoint ({ i32, i32 }* @huge.hwasan.descriptor.1 to i64)), i64 16777200) to i32), i32 -1375731680 }, section "hwasan_globals", !associated [[HUGE]]
+
+; CHECK30: @four = alias i32, inttoptr (i64 add (i64 ptrtoint ({ i32, [12 x i8] }* @four.hwasan to i64), i64 -6052837899185946624) to i32*)
+; CHECK30: @sixteen = alias [16 x i8], inttoptr (i64 add (i64 ptrtoint ([16 x i8]* @sixteen.hwasan to i64), i64 -5980780305148018688) to [16 x i8]*)
+; CHECK30: @huge = alias [16777232 x i8], inttoptr (i64 add (i64 ptrtoint ([16777232 x i8]* @huge.hwasan to i64), i64 -5908722711110090752) to [16777232 x i8]*)
+
+; CHECK30: [[NOTE]] = !{{{{}} i32, i32, i32, [8 x i8], i32, i32 }* @hwasan.note}
+; CHECK30: [[FOUR]] = !{{{{}} i32, [12 x i8] }* @four.hwasan}
+; CHECK30: [[SIXTEEN]] = !{[16 x i8]* @sixteen.hwasan}
+; CHECK30: [[HUGE]] = !{[16777232 x i8]* @huge.hwasan}
+
+source_filename = "foo"
+
+@four = global i32 1
+@sixteen = global [16 x i8] zeroinitializer
+@huge = global [16777232 x i8] zeroinitializer
Index: llvm/trunk/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
===================================================================
--- llvm/trunk/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
+++ llvm/trunk/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 @@
 #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 @@
     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 @@
 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 @@
   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 @@
     bool InTls;
 
     void init(Triple &TargetTriple);
-    unsigned getAllocaAlignment() const { return 1U << Scale; }
+    unsigned getObjectAlignment() const { return 1U << Scale; }
   };
   ShadowMapping Mapping;
 
@@ -245,6 +254,7 @@
   Type *Int8PtrTy;
   Type *Int8Ty;
   Type *Int32Ty;
+  Type *Int64Ty = Type::getInt64Ty(M.getContext());
 
   bool CompileKernel;
   bool Recover;
@@ -332,7 +342,7 @@
 /// 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 @@
               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 @@
 
 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 @@
       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 @@
 
       // 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 @@
   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 @@
   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) {
Index: llvm/trunk/include/llvm/BinaryFormat/ELF.h
===================================================================
--- llvm/trunk/include/llvm/BinaryFormat/ELF.h
+++ llvm/trunk/include/llvm/BinaryFormat/ELF.h
@@ -1416,6 +1416,11 @@
   NT_SIGINFO = 0x53494749,
 };
 
+// LLVM-specific notes.
+enum {
+  NT_LLVM_HWASAN_GLOBALS = 3,
+};
+
 // GNU note types
 enum {
   NT_GNU_ABI_TAG = 1,
Index: cfe/trunk/lib/Driver/SanitizerArgs.cpp
===================================================================
--- cfe/trunk/lib/Driver/SanitizerArgs.cpp
+++ cfe/trunk/lib/Driver/SanitizerArgs.cpp
@@ -1013,6 +1013,11 @@
     CmdArgs.push_back(Args.MakeArgString("hwasan-abi=" + HwasanAbi));
   }
 
+  if (Sanitizers.has(SanitizerKind::HWAddress)) {
+    CmdArgs.push_back("-target-feature");
+    CmdArgs.push_back("+tagged-globals");
+  }
+
   // MSan: Workaround for PR16386.
   // ASan: This is mainly to help LSan with cases such as
   // https://github.com/google/sanitizers/issues/373
Index: cfe/trunk/test/Driver/fsanitize.c
===================================================================
--- cfe/trunk/test/Driver/fsanitize.c
+++ cfe/trunk/test/Driver/fsanitize.c
@@ -843,7 +843,9 @@
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=hwaddress -fsanitize-hwaddress-abi=platform %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-HWASAN-PLATFORM-ABI
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=hwaddress -fsanitize-hwaddress-abi=foo %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-HWASAN-FOO-ABI
 // CHECK-HWASAN-INTERCEPTOR-ABI: "-default-function-attr" "hwasan-abi=interceptor"
+// CHECK-HWASAN-INTERCEPTOR-ABI: "-target-feature" "+tagged-globals"
 // CHECK-HWASAN-PLATFORM-ABI: "-default-function-attr" "hwasan-abi=platform"
+// CHECK-HWASAN-PLATFORM-ABI: "-target-feature" "+tagged-globals"
 // CHECK-HWASAN-FOO-ABI: error: invalid value 'foo' in '-fsanitize-hwaddress-abi=foo'
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=address,pointer-compare,pointer-subtract %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-POINTER-ALL
Index: compiler-rt/trunk/lib/hwasan/hwasan_interface_internal.h
===================================================================
--- compiler-rt/trunk/lib/hwasan/hwasan_interface_internal.h
+++ compiler-rt/trunk/lib/hwasan/hwasan_interface_internal.h
@@ -16,6 +16,7 @@
 
 #include "sanitizer_common/sanitizer_internal_defs.h"
 #include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include <link.h>
 
 extern "C" {
 
@@ -25,6 +26,14 @@
 SANITIZER_INTERFACE_ATTRIBUTE
 void __hwasan_init();
 
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                             ElfW(Half) phnum);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                               ElfW(Half) phnum);
+
 using __sanitizer::uptr;
 using __sanitizer::sptr;
 using __sanitizer::uu64;
Index: compiler-rt/trunk/lib/hwasan/hwasan.h
===================================================================
--- compiler-rt/trunk/lib/hwasan/hwasan.h
+++ compiler-rt/trunk/lib/hwasan/hwasan.h
@@ -75,7 +75,6 @@
 bool ProtectRange(uptr beg, uptr end);
 bool InitShadow();
 void InitThreads();
-void InitInstrumentation();
 void MadviseShadow();
 char *GetProcSelfMaps();
 void InitializeInterceptors();
Index: compiler-rt/trunk/lib/hwasan/hwasan.cpp
===================================================================
--- compiler-rt/trunk/lib/hwasan/hwasan.cpp
+++ compiler-rt/trunk/lib/hwasan/hwasan.cpp
@@ -193,27 +193,12 @@
 void UpdateMemoryUsage() {}
 #endif
 
-// Prepare to run instrumented code on the main thread.
-void InitInstrumentation() {
-  if (hwasan_instrumentation_inited) return;
-
-  if (!InitShadow()) {
-    Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
-    DumpProcessMap();
-    Die();
-  }
-
-  InitThreads();
-  hwasanThreadList().CreateCurrentThread();
-
-  hwasan_instrumentation_inited = 1;
-}
-
 } // namespace __hwasan
 
+using namespace __hwasan;
+
 void __sanitizer::BufferedStackTrace::UnwindImpl(
     uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
-  using namespace __hwasan;
   Thread *t = GetCurrentThread();
   if (!t) {
     // the thread is still being created.
@@ -231,9 +216,85 @@
     Unwind(max_depth, pc, 0, context, 0, 0, false);
 }
 
-// Interface.
+struct hwasan_global {
+  s32 gv_relptr;
+  u32 info;
+};
+
+static void InitGlobals(const hwasan_global *begin, const hwasan_global *end) {
+  for (auto *desc = begin; desc != end; ++desc) {
+    uptr gv = reinterpret_cast<uptr>(desc) + desc->gv_relptr;
+    uptr size = desc->info & 0xffffff;
+    uptr full_granule_size = RoundDownTo(size, 16);
+    u8 tag = desc->info >> 24;
+    TagMemoryAligned(gv, full_granule_size, tag);
+    if (size % 16)
+      TagMemoryAligned(gv + full_granule_size, 16, size % 16);
+  }
+}
 
-using namespace __hwasan;
+enum { NT_LLVM_HWASAN_GLOBALS = 3 };
+
+struct hwasan_global_note {
+  s32 begin_relptr;
+  s32 end_relptr;
+};
+
+static void InitGlobalsFromPhdrs(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                                 ElfW(Half) phnum) {
+  for (; phnum != 0; ++phdr, --phnum) {
+    if (phdr->p_type != PT_NOTE)
+      continue;
+    const char *note = reinterpret_cast<const char *>(base + phdr->p_vaddr);
+    const char *nend = note + phdr->p_memsz;
+    while (note < nend) {
+      auto *nhdr = reinterpret_cast<const ElfW(Nhdr) *>(note);
+      const char *name = note + sizeof(ElfW(Nhdr));
+      const char *desc = name + nhdr->n_namesz;
+      if (nhdr->n_type != NT_LLVM_HWASAN_GLOBALS ||
+          internal_strcmp(name, "LLVM") != 0) {
+        note = desc + nhdr->n_descsz;
+        continue;
+      }
+
+      auto *global_note = reinterpret_cast<const hwasan_global_note *>(desc);
+      auto *global_begin = reinterpret_cast<const hwasan_global *>(
+          note + global_note->begin_relptr);
+      auto *global_end = reinterpret_cast<const hwasan_global *>(
+          note + global_note->end_relptr);
+      InitGlobals(global_begin, global_end);
+      return;
+    }
+  }
+}
+
+static void InitLoadedGlobals() {
+  dl_iterate_phdr(
+      [](dl_phdr_info *info, size_t size, void *data) {
+        InitGlobalsFromPhdrs(info->dlpi_addr, info->dlpi_phdr,
+                             info->dlpi_phnum);
+        return 0;
+      },
+      nullptr);
+}
+
+// Prepare to run instrumented code on the main thread.
+static void InitInstrumentation() {
+  if (hwasan_instrumentation_inited) return;
+
+  if (!InitShadow()) {
+    Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
+    DumpProcessMap();
+    Die();
+  }
+
+  InitThreads();
+  hwasanThreadList().CreateCurrentThread();
+
+  hwasan_instrumentation_inited = 1;
+}
+
+// Interface.
 
 uptr __hwasan_shadow_memory_dynamic_address;  // Global interface symbol.
 
@@ -244,6 +305,17 @@
 void __hwasan_init_static() {
   InitShadowGOT();
   InitInstrumentation();
+
+  // In the non-static code path we call dl_iterate_phdr here. But at this point
+  // libc might not have been initialized enough for dl_iterate_phdr to work.
+  // Fortunately, since this is a statically linked executable we can use the
+  // linker-defined symbol __ehdr_start to find the only relevant set of phdrs.
+  extern ElfW(Ehdr) __ehdr_start;
+  InitGlobalsFromPhdrs(
+      0,
+      reinterpret_cast<const ElfW(Phdr) *>(
+          reinterpret_cast<const char *>(&__ehdr_start) + __ehdr_start.e_phoff),
+      __ehdr_start.e_phnum);
 }
 
 void __hwasan_init() {
@@ -267,6 +339,7 @@
   DisableCoreDumperIfNecessary();
 
   InitInstrumentation();
+  InitLoadedGlobals();
 
   // Needs to be called here because flags()->random_tags might not have been
   // initialized when InitInstrumentation() was called.
@@ -301,6 +374,18 @@
   hwasan_inited = 1;
 }
 
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                             ElfW(Half) phnum) {
+  InitGlobalsFromPhdrs(base, phdr, phnum);
+}
+
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                               ElfW(Half) phnum) {
+  for (; phnum != 0; ++phdr, --phnum)
+    if (phdr->p_type == PT_LOAD)
+      TagMemory(base + phdr->p_vaddr, phdr->p_memsz, 0);
+}
+
 void __hwasan_print_shadow(const void *p, uptr sz) {
   uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
   uptr shadow_first = MemToShadow(ptr_raw);
Index: compiler-rt/trunk/lib/hwasan/hwasan_report.cpp
===================================================================
--- compiler-rt/trunk/lib/hwasan/hwasan_report.cpp
+++ compiler-rt/trunk/lib/hwasan/hwasan_report.cpp
@@ -278,6 +278,31 @@
       Printf("%s", d.Default());
       GetStackTraceFromId(chunk.GetAllocStackId()).Print();
       num_descriptions_printed++;
+    } else {
+      // Check whether the address points into a loaded library. If so, this is
+      // most likely a global variable.
+      const char *module_name;
+      uptr module_address;
+      Symbolizer *sym = Symbolizer::GetOrInit();
+      if (sym->GetModuleNameAndOffsetForPC(mem, &module_name,
+                                           &module_address)) {
+        DataInfo info;
+        if (sym->SymbolizeData(mem, &info) && info.start) {
+          Printf(
+              "%p is located %zd bytes to the %s of %zd-byte global variable "
+              "%s [%p,%p) in %s\n",
+              untagged_addr,
+              candidate == left ? untagged_addr - (info.start + info.size)
+                                : info.start - untagged_addr,
+              candidate == left ? "right" : "left", info.size, info.name,
+              info.start, info.start + info.size, module_name);
+        } else {
+          Printf("%p is located to the %s of a global variable in (%s+0x%x)\n",
+                 untagged_addr, candidate == left ? "right" : "left",
+                 module_name, module_address);
+        }
+        num_descriptions_printed++;
+      }
     }
   }
 
Index: compiler-rt/trunk/test/hwasan/lit.cfg.py
===================================================================
--- compiler-rt/trunk/test/hwasan/lit.cfg.py
+++ compiler-rt/trunk/test/hwasan/lit.cfg.py
@@ -11,7 +11,12 @@
 # Setup default compiler flags used with -fsanitize=memory option.
 clang_cflags = [config.target_cflags] + config.debug_info_flags
 clang_cxxflags = config.cxx_mode_flags + clang_cflags
-clang_hwasan_cflags = ["-fsanitize=hwaddress"] + clang_cflags
+clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-globals"] + clang_cflags
+if config.target_arch == 'x86_64':
+  # This does basically the same thing as tagged-globals on aarch64. Because
+  # the x86_64 implementation is for testing purposes only there is no
+  # equivalent target feature implemented on x86_64.
+  clang_hwasan_cflags += ["-mcmodel=large"]
 clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags
 
 def build_invocation(compile_flags):
Index: compiler-rt/trunk/test/hwasan/TestCases/global.c
===================================================================
--- compiler-rt/trunk/test/hwasan/TestCases/global.c
+++ compiler-rt/trunk/test/hwasan/TestCases/global.c
@@ -0,0 +1,17 @@
+// RUN: %clang_hwasan %s -o %t
+// RUN: %run %t 0
+// RUN: not %run %t 1 2>&1 | FileCheck --check-prefixes=CHECK,RSYM %s
+// RUN: not %env_hwasan_opts=symbolize=0 %run %t 1 2>&1 | FileCheck --check-prefixes=CHECK,RNOSYM %s
+// RUN: not %run %t -1 2>&1 | FileCheck --check-prefixes=CHECK,LSYM %s
+// RUN: not %env_hwasan_opts=symbolize=0 %run %t -1 2>&1 | FileCheck --check-prefixes=CHECK,LNOSYM %s
+
+int x = 1;
+
+int main(int argc, char **argv) {
+  // RSYM: is located 0 bytes to the right of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp
+  // RNOSYM: is located to the right of a global variable in ({{.*}}global.c.tmp+{{.*}})
+  // LSYM: is located 4 bytes to the left of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp
+  // LNOSYM: is located to the left of a global variable in ({{.*}}global.c.tmp+{{.*}})
+  // CHECK-NOT: can not describe
+  (&x)[atoi(argv[1])] = 1;
+}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to