forked from OSchip/llvm-project
[hwasan] add stack frame descriptions.
Summary: At compile-time, create an array of {PC,HumanReadableStackFrameDescription} for every function that has an instrumented frame, and pass this array to the run-time at the module-init time. Similar to how we handle pc-table in SanitizerCoverage. The run-time is dummy, will add the actual logic in later commits. Reviewers: morehouse, eugenis Reviewed By: eugenis Subscribers: srhines, llvm-commits, kubamracek Differential Revision: https://reviews.llvm.org/D53227 llvm-svn: 344985
This commit is contained in:
parent
9d7de097d4
commit
af95597c3c
|
@ -220,6 +220,22 @@ void UpdateMemoryUsage() {
|
||||||
void UpdateMemoryUsage() {}
|
void UpdateMemoryUsage() {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct FrameDescription {
|
||||||
|
uptr PC;
|
||||||
|
const char *Descr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitFrameDescriptors(uptr b, uptr e) {
|
||||||
|
FrameDescription *beg = reinterpret_cast<FrameDescription *>(b);
|
||||||
|
FrameDescription *end = reinterpret_cast<FrameDescription *>(e);
|
||||||
|
// Must have at least one entry, which we can use for a linked list.
|
||||||
|
CHECK_GE(end - beg, 1U);
|
||||||
|
if (Verbosity()) {
|
||||||
|
for (FrameDescription *frame_descr = beg; frame_descr < end; frame_descr++)
|
||||||
|
Printf("Frame: %p %s\n", frame_descr->PC, frame_descr->Descr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace __hwasan
|
} // namespace __hwasan
|
||||||
|
|
||||||
// Interface.
|
// Interface.
|
||||||
|
@ -238,6 +254,10 @@ void __hwasan_shadow_init() {
|
||||||
hwasan_shadow_inited = 1;
|
hwasan_shadow_inited = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __hwasan_init_frames(uptr beg, uptr end) {
|
||||||
|
InitFrameDescriptors(beg, end);
|
||||||
|
}
|
||||||
|
|
||||||
void __hwasan_init() {
|
void __hwasan_init() {
|
||||||
CHECK(!hwasan_init_is_running);
|
CHECK(!hwasan_init_is_running);
|
||||||
if (hwasan_inited) return;
|
if (hwasan_inited) return;
|
||||||
|
|
|
@ -36,6 +36,9 @@ using __sanitizer::u32;
|
||||||
using __sanitizer::u16;
|
using __sanitizer::u16;
|
||||||
using __sanitizer::u8;
|
using __sanitizer::u8;
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
void __hwasan_init_frames(uptr, uptr);
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
extern uptr __hwasan_shadow_memory_dynamic_address;
|
extern uptr __hwasan_shadow_memory_dynamic_address;
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||||
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||||
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
|
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
|
@ -146,6 +147,11 @@ static cl::opt<bool>
|
||||||
cl::desc("Record stack frames with tagged allocations "
|
cl::desc("Record stack frames with tagged allocations "
|
||||||
"in a thread-local ring buffer"),
|
"in a thread-local ring buffer"),
|
||||||
cl::Hidden, cl::init(true));
|
cl::Hidden, cl::init(true));
|
||||||
|
static cl::opt<bool>
|
||||||
|
ClCreateFrameDescriptions("hwasan-create-frame-descriptions",
|
||||||
|
cl::desc("create static frame descriptions"),
|
||||||
|
cl::Hidden, cl::init(true));
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// An instrumentation pass implementing detection of addressability bugs
|
/// An instrumentation pass implementing detection of addressability bugs
|
||||||
|
@ -198,8 +204,27 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LLVMContext *C;
|
LLVMContext *C;
|
||||||
|
std::string CurModuleUniqueId;
|
||||||
Triple TargetTriple;
|
Triple TargetTriple;
|
||||||
|
|
||||||
|
// 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 createFrameString(ArrayRef<AllocaInst*> Allocas);
|
||||||
|
void createFrameGlobal(Function &F, const std::string &FrameString);
|
||||||
|
// Get the section name for frame descriptions. Currently ELF-only.
|
||||||
|
const char *getFrameSection() { return "__hwasan_frames"; }
|
||||||
|
const char *getFrameSectionBeg() { return "__start___hwasan_frames"; }
|
||||||
|
const char *getFrameSectionEnd() { return "__stop___hwasan_frames"; }
|
||||||
|
GlobalVariable *createFrameSectionBound(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:
|
/// This struct defines the shadow mapping using the rule:
|
||||||
/// shadow = (mem >> Scale) + Offset.
|
/// shadow = (mem >> Scale) + Offset.
|
||||||
/// If InGlobal is true, then
|
/// If InGlobal is true, then
|
||||||
|
@ -207,7 +232,7 @@ private:
|
||||||
/// shadow = (mem >> Scale) + &__hwasan_shadow
|
/// shadow = (mem >> Scale) + &__hwasan_shadow
|
||||||
/// If InTls is true, then
|
/// If InTls is true, then
|
||||||
/// extern char *__hwasan_tls;
|
/// extern char *__hwasan_tls;
|
||||||
/// shadow = (mem >> Scale) + align_up(__hwasan_shadow, kShadowBaseAlignment)
|
/// shadow = (mem>>Scale) + align_up(__hwasan_shadow, kShadowBaseAlignment)
|
||||||
struct ShadowMapping {
|
struct ShadowMapping {
|
||||||
int Scale;
|
int Scale;
|
||||||
uint64_t Offset;
|
uint64_t Offset;
|
||||||
|
@ -271,6 +296,7 @@ bool HWAddressSanitizer::doInitialization(Module &M) {
|
||||||
Mapping.init(TargetTriple);
|
Mapping.init(TargetTriple);
|
||||||
|
|
||||||
C = &(M.getContext());
|
C = &(M.getContext());
|
||||||
|
CurModuleUniqueId = getUniqueModuleId(&M);
|
||||||
IRBuilder<> IRB(*C);
|
IRBuilder<> IRB(*C);
|
||||||
IntptrTy = IRB.getIntPtrTy(DL);
|
IntptrTy = IRB.getIntPtrTy(DL);
|
||||||
Int8PtrTy = IRB.getInt8PtrTy();
|
Int8PtrTy = IRB.getInt8PtrTy();
|
||||||
|
@ -285,6 +311,21 @@ bool HWAddressSanitizer::doInitialization(Module &M) {
|
||||||
/*InitArgs=*/{});
|
/*InitArgs=*/{});
|
||||||
appendToGlobalCtors(M, HwasanCtorFunction, 0);
|
appendToGlobalCtors(M, HwasanCtorFunction, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a call to __hwasan_init_frames.
|
||||||
|
if (HwasanCtorFunction) {
|
||||||
|
// Create a dummy frame description 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).
|
||||||
|
createFrameGlobal(*HwasanCtorFunction, "");
|
||||||
|
IRBuilder<> IRBCtor(HwasanCtorFunction->getEntryBlock().getTerminator());
|
||||||
|
IRBCtor.CreateCall(
|
||||||
|
declareSanitizerInitFunction(M, "__hwasan_init_frames",
|
||||||
|
{Int8PtrTy, Int8PtrTy}),
|
||||||
|
{createFrameSectionBound(M, Int8Ty, getFrameSectionBeg()),
|
||||||
|
createFrameSectionBound(M, Int8Ty, getFrameSectionEnd())});
|
||||||
|
}
|
||||||
|
|
||||||
if (!TargetTriple.isAndroid())
|
if (!TargetTriple.isAndroid())
|
||||||
appendToCompilerUsed(
|
appendToCompilerUsed(
|
||||||
M, ThreadPtrGlobal = new GlobalVariable(
|
M, ThreadPtrGlobal = new GlobalVariable(
|
||||||
|
@ -676,6 +717,36 @@ Value *HWAddressSanitizer::getHwasanThreadSlotPtr(IRBuilder<> &IRB, Type *Ty) {
|
||||||
return nullptr;
|
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::createFrameString(ArrayRef<AllocaInst *> Allocas) {
|
||||||
|
std::ostringstream Descr;
|
||||||
|
for (auto AI : Allocas)
|
||||||
|
Descr << getAllocaSizeInBytes(*AI) << " " << AI->getName().str() << "; ";
|
||||||
|
return Descr.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a global in the frame section which consists of two pointers:
|
||||||
|
// the function PC and the frame string constant.
|
||||||
|
void HWAddressSanitizer::createFrameGlobal(Function &F,
|
||||||
|
const std::string &FrameString) {
|
||||||
|
Module &M = *F.getParent();
|
||||||
|
auto DescrGV = createPrivateGlobalForString(M, FrameString, 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(getFrameSection());
|
||||||
|
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,
|
Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB,
|
||||||
bool WithFrameRecord) {
|
bool WithFrameRecord) {
|
||||||
if (!Mapping.InTls)
|
if (!Mapping.InTls)
|
||||||
|
@ -838,6 +909,9 @@ bool HWAddressSanitizer::runOnFunction(Function &F) {
|
||||||
if (AllocasToInstrument.empty() && ToInstrument.empty())
|
if (AllocasToInstrument.empty() && ToInstrument.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (ClCreateFrameDescriptions && !AllocasToInstrument.empty())
|
||||||
|
createFrameGlobal(F, createFrameString(AllocasToInstrument));
|
||||||
|
|
||||||
initializeCallbacks(*F.getParent());
|
initializeCallbacks(*F.getParent());
|
||||||
|
|
||||||
assert(!LocalDynamicShadow);
|
assert(!LocalDynamicShadow);
|
||||||
|
|
|
@ -354,5 +354,6 @@ entry:
|
||||||
|
|
||||||
; CHECK: define internal void @hwasan.module_ctor() {
|
; CHECK: define internal void @hwasan.module_ctor() {
|
||||||
; CHECK-NEXT: call void @__hwasan_init()
|
; CHECK-NEXT: call void @__hwasan_init()
|
||||||
|
; CHECK-NEXT: call void @__hwasan_init_frames(
|
||||||
; CHECK-NEXT: ret void
|
; CHECK-NEXT: ret void
|
||||||
; CHECK-NEXT: }
|
; CHECK-NEXT: }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -199,5 +199,6 @@ entry:
|
||||||
|
|
||||||
; CHECK: define internal void @hwasan.module_ctor() {
|
; CHECK: define internal void @hwasan.module_ctor() {
|
||||||
; CHECK-NEXT: call void @__hwasan_init()
|
; CHECK-NEXT: call void @__hwasan_init()
|
||||||
|
; CHECK-NEXT: call void @__hwasan_init_frames(
|
||||||
; CHECK-NEXT: ret void
|
; CHECK-NEXT: ret void
|
||||||
; CHECK-NEXT: }
|
; CHECK-NEXT: }
|
||||||
|
|
Loading…
Reference in New Issue