Index: lib/Transforms/Instrumentation/HWAddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -44,6 +44,7 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" +#include using namespace llvm; @@ -146,6 +147,11 @@ cl::desc("Record stack frames with tagged allocations " "in a thread-local ring buffer"), cl::Hidden, cl::init(true)); +static cl::opt + ClCreateFrameDescriptions("hwasan-create-frame-descriptions", + cl::desc("create static frame descriptions"), + cl::Hidden, cl::init(true)); + namespace { /// An instrumentation pass implementing detection of addressability bugs @@ -198,8 +204,26 @@ private: LLVMContext *C; + std::string CurModuleUniqueId; Triple TargetTriple; + // FD (frame description) is a way to pass names/sizes of local variables + // to the run-time w/o adding extra executable code in every function. + // We do this by creating a separate section with {PC,Descr} pairs and passing + // the section beg/end to __hwasan_init_frames() at module init time. + std::string createFDString(ArrayRef Allocas); + void createFD(Function &F, const std::string &FDString); + // Get the section name for FD. Currently ELF-only. + const char *getFDSection() { return "__hwasan_frames"; } + const char *getFDSectionBeg() { return "__start___hwasan_frames"; } + const char *getFDSectionEnd() { return "__stop___hwasan_frames"; } + GlobalVariable *createFDSectionBound(Module &M, Type *Ty, const char *Name) { + auto GV = new GlobalVariable(M, Ty, false, GlobalVariable::ExternalLinkage, + nullptr, Name); + GV->setVisibility(GlobalValue::HiddenVisibility); + return GV; + } + /// This struct defines the shadow mapping using the rule: /// shadow = (mem >> Scale) + Offset. /// If InGlobal is true, then @@ -271,6 +295,7 @@ Mapping.init(TargetTriple); C = &(M.getContext()); + CurModuleUniqueId = getUniqueModuleId(&M); IRBuilder<> IRB(*C); IntptrTy = IRB.getIntPtrTy(DL); Int8PtrTy = IRB.getInt8PtrTy(); @@ -285,6 +310,20 @@ /*InitArgs=*/{}); appendToGlobalCtors(M, HwasanCtorFunction, 0); } + + // Create a call to __hwasan_init_frames. + if (HwasanCtorFunction) { + // Create a dummy FD for the CTOR function. + // W/o it we would have to create the call to __hwasan_init_frames after + // all functions are instrumented (i.e. need to have a ModulePass). + createFD(*HwasanCtorFunction, ""); + IRBuilder<> IRBCtor(HwasanCtorFunction->getEntryBlock().getTerminator()); + IRBCtor.CreateCall(declareSanitizerInitFunction(M, "__hwasan_init_frames", + {Int8PtrTy, Int8PtrTy}), + {createFDSectionBound(M, Int8Ty, getFDSectionBeg()), + createFDSectionBound(M, Int8Ty, getFDSectionEnd())}); + } + if (!TargetTriple.isAndroid()) appendToCompilerUsed( M, ThreadPtrGlobal = new GlobalVariable( @@ -676,6 +715,34 @@ return nullptr; } +// Creates a string with a description of the stack frame (set of Allocas). +// The string is intended to be human readable. +// The current form is: Size1 Name1; Size2 Name2; ... +std::string HWAddressSanitizer::createFDString(ArrayRef Allocas) { + std::ostringstream Descr; + for (auto AI : Allocas) + Descr << getAllocaSizeInBytes(*AI) << " " << AI->getName().str() << "; "; + return Descr.str(); +} + +// Creates a global in the FD section which consists of two pointers: +// the function PC and the FD string constant. +void HWAddressSanitizer::createFD(Function &F, const std::string &FDString) { + Module &M = *F.getParent(); + auto DescrGV = createPrivateGlobalForString(M, FDString, true); + auto PtrPairTy = StructType::get(F.getType(), DescrGV->getType()); + auto GV = new GlobalVariable( + M, PtrPairTy, /*isConstantGlobal*/ true, GlobalVariable::PrivateLinkage, + ConstantStruct::get(PtrPairTy, (Constant *)&F, (Constant *)DescrGV), + "__hwasan"); + GV->setSection(getFDSection()); + appendToCompilerUsed(M, GV); + // Put GV into the F's Comadat so that if F is deleted GV can be deleted too. + if (&F != HwasanCtorFunction) + if (auto Comdat = GetOrCreateFunctionComdat(F, CurModuleUniqueId)) + GV->setComdat(Comdat); +} + Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord) { if (!Mapping.InTls) @@ -838,6 +905,9 @@ if (AllocasToInstrument.empty() && ToInstrument.empty()) return false; + if (ClCreateFrameDescriptions && !AllocasToInstrument.empty()) + createFD(F, createFDString(AllocasToInstrument)); + initializeCallbacks(*F.getParent()); assert(!LocalDynamicShadow); Index: projects/compiler-rt/lib/hwasan/hwasan.cc =================================================================== --- projects/compiler-rt/lib/hwasan/hwasan.cc +++ projects/compiler-rt/lib/hwasan/hwasan.cc @@ -220,6 +220,22 @@ void UpdateMemoryUsage() {} #endif +struct FrameDescription { + uptr PC; + const char *Descr; +}; + +void InitFrameDescriptors(uptr b, uptr e) { + FrameDescription *beg = reinterpret_cast(b); + FrameDescription *end = reinterpret_cast(e); + // Must have at least one entry, which we can use for a linked list. + CHECK_GE(end - beg, 1U); + if (Verbosity()) { + for (FrameDescription *fd = beg; fd < end; fd++) + Printf("FD: %p %s\n", fd->PC, fd->Descr); + } +} + } // namespace __hwasan // Interface. @@ -238,6 +254,10 @@ hwasan_shadow_inited = 1; } +void __hwasan_init_frames(uptr beg, uptr end) { + InitFrameDescriptors(beg, end); +} + void __hwasan_init() { CHECK(!hwasan_init_is_running); if (hwasan_inited) return; Index: projects/compiler-rt/lib/hwasan/hwasan_interface_internal.h =================================================================== --- projects/compiler-rt/lib/hwasan/hwasan_interface_internal.h +++ projects/compiler-rt/lib/hwasan/hwasan_interface_internal.h @@ -37,6 +37,9 @@ using __sanitizer::u8; SANITIZER_INTERFACE_ATTRIBUTE +void __hwasan_init_frames(uptr, uptr); + +SANITIZER_INTERFACE_ATTRIBUTE extern uptr __hwasan_shadow_memory_dynamic_address; SANITIZER_INTERFACE_ATTRIBUTE Index: test/Instrumentation/HWAddressSanitizer/basic.ll =================================================================== --- test/Instrumentation/HWAddressSanitizer/basic.ll +++ test/Instrumentation/HWAddressSanitizer/basic.ll @@ -354,5 +354,6 @@ ; CHECK: define internal void @hwasan.module_ctor() { ; CHECK-NEXT: call void @__hwasan_init() +; CHECK-NEXT: call void @__hwasan_init_frames( ; CHECK-NEXT: ret void ; CHECK-NEXT: } Index: test/Instrumentation/HWAddressSanitizer/frame-descriptor.ll =================================================================== --- /dev/null +++ test/Instrumentation/HWAddressSanitizer/frame-descriptor.ll @@ -0,0 +1,27 @@ +; Test frame descriptors +; +; 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" + +declare void @use32(i32*, i64*) + +define void @test_alloca() sanitize_hwaddress { +entry: + %XYZ = alloca i32, align 4 + %ABC = alloca i64, align 4 + call void @use32(i32* nonnull %XYZ, i64 *nonnull %ABC) + ret void +} + +; CHECK: @[[STR:[0-9]*]] = private unnamed_addr constant [15 x i8] c"4 XYZ; 8 ABC; \00", align 1 +; CHECK: private constant { void ()*, [15 x i8]* } { void ()* @test_alloca, [15 x i8]* @[[STR]] }, section "__hwasan_frames", comdat($test_alloca) + +; CHECK-LABEL: @test_alloca( +; CHECK: ret void + +; CHECK-LABEL: @hwasan.module_ctor +; CHECK: call void @__hwasan_init_frames(i8* @__start___hwasan_frames, i8* @__stop___hwasan_frames) +; CHECK: ret void + Index: test/Instrumentation/HWAddressSanitizer/with-calls.ll =================================================================== --- test/Instrumentation/HWAddressSanitizer/with-calls.ll +++ test/Instrumentation/HWAddressSanitizer/with-calls.ll @@ -199,5 +199,6 @@ ; CHECK: define internal void @hwasan.module_ctor() { ; CHECK-NEXT: call void @__hwasan_init() +; CHECK-NEXT: call void @__hwasan_init_frames( ; CHECK-NEXT: ret void ; CHECK-NEXT: }