Skip to content
This repository has been archived by the owner on Jan 1, 2023. It is now read-only.

Commit

Permalink
hwasan: Untag unwound stack frames by wrapping personality functions.
Browse files Browse the repository at this point in the history
One problem with untagging memory in landing pads is that it only works
correctly if the function that catches the exception is instrumented.
If the function is uninstrumented, we have no opportunity to untag the
memory.

To address this, replace landing pad instrumentation with personality function
wrapping. Each function with an instrumented stack has its personality function
replaced with a wrapper provided by the runtime. Functions that did not have
a personality function to begin with also get wrappers if they may be unwound
past. As the unwinder calls personality functions during stack unwinding,
the original personality function is called and the function's stack frame is
untagged by the wrapper if the personality function instructs the unwinder
to keep unwinding. If unwinding stops at a landing pad, the function is
still responsible for untagging its stack frame if it resumes unwinding.

The old landing pad mechanism is preserved for compatibility with old runtimes.

Differential Revision: https://reviews.llvm.org/D66377

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@369721 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
pcc committed Aug 23, 2019
1 parent a7a4cf0 commit 3335b9d
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 12 deletions.
110 changes: 102 additions & 8 deletions lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
Expand Down Expand Up @@ -55,6 +56,8 @@ using namespace llvm;
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 kHwasanPersonalityThunkName =
"__hwasan_personality_thunk";

static const char *const kHwasanShadowMemoryDynamicAddress =
"__hwasan_shadow_memory_dynamic_address";
Expand Down Expand Up @@ -160,8 +163,12 @@ static cl::opt<bool>

static cl::opt<bool>
ClInstrumentLandingPads("hwasan-instrument-landing-pads",
cl::desc("instrument landing pads"), cl::Hidden,
cl::init(true));
cl::desc("instrument landing pads"), cl::Hidden,
cl::init(false));

static cl::opt<bool> ClInstrumentPersonalityFunctions(
"hwasan-instrument-personality-functions",
cl::desc("instrument personality functions"), cl::Hidden, cl::init(false));

static cl::opt<bool> ClInlineAllChecks("hwasan-inline-all-checks",
cl::desc("inline all checks"),
Expand Down Expand Up @@ -224,6 +231,8 @@ class HWAddressSanitizer {
void instrumentGlobal(GlobalVariable *GV, uint8_t Tag);
void instrumentGlobals();

void instrumentPersonalityFunctions();

private:
LLVMContext *C;
Module &M;
Expand All @@ -250,6 +259,7 @@ class HWAddressSanitizer {
};
ShadowMapping Mapping;

Type *VoidTy = Type::getVoidTy(M.getContext());
Type *IntptrTy;
Type *Int8PtrTy;
Type *Int8Ty;
Expand All @@ -258,6 +268,7 @@ class HWAddressSanitizer {

bool CompileKernel;
bool Recover;
bool InstrumentLandingPads;

Function *HwasanCtorFunction;

Expand Down Expand Up @@ -373,14 +384,27 @@ void HWAddressSanitizer::initializeModule() {
});

// 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 =
// global or personality function instrumentation. On other platforms we
// currently require using the latest version of the runtime.
bool NewRuntime =
!TargetTriple.isAndroid() || !TargetTriple.isAndroidVersionLT(30);
if (ClGlobals.getNumOccurrences())
InstrumentGlobals = ClGlobals;

bool InstrumentGlobals =
ClGlobals.getNumOccurrences() ? ClGlobals : NewRuntime;
if (InstrumentGlobals)
instrumentGlobals();

// If we don't have personality function support, fall back to landing pads.
InstrumentLandingPads = ClInstrumentLandingPads.getNumOccurrences()
? ClInstrumentLandingPads
: !NewRuntime;

bool InstrumentPersonalityFunctions =
ClInstrumentPersonalityFunctions.getNumOccurrences()
? ClInstrumentPersonalityFunctions
: NewRuntime;
if (InstrumentPersonalityFunctions)
instrumentPersonalityFunctions();
}

if (!TargetTriple.isAndroid()) {
Expand Down Expand Up @@ -1092,7 +1116,7 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {
if (auto *Alloca = dyn_cast_or_null<AllocaInst>(DDI->getAddress()))
AllocaDeclareMap[Alloca].push_back(DDI);

if (ClInstrumentLandingPads && isa<LandingPadInst>(Inst))
if (InstrumentLandingPads && isa<LandingPadInst>(Inst))
LandingPadVec.push_back(&Inst);

Value *MaybeMask = nullptr;
Expand All @@ -1111,6 +1135,13 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {
if (!LandingPadVec.empty())
instrumentLandingPads(LandingPadVec);

if (AllocasToInstrument.empty() && F.hasPersonalityFn() &&
F.getPersonalityFn()->getName() == kHwasanPersonalityThunkName) {
// __hwasan_personality_thunk is a no-op for functions without an
// instrumented stack, so we can drop it.
F.setPersonalityFn(nullptr);
}

if (AllocasToInstrument.empty() && ToInstrument.empty())
return false;

Expand Down Expand Up @@ -1386,6 +1417,69 @@ void HWAddressSanitizer::instrumentGlobals() {
}
}

void HWAddressSanitizer::instrumentPersonalityFunctions() {
// We need to untag stack frames as we unwind past them. That is the job of
// the personality function wrapper, which either wraps an existing
// personality function or acts as a personality function on its own. Each
// function that has a personality function or that can be unwound past has
// its personality function changed to a thunk that calls the personality
// function wrapper in the runtime.
MapVector<Constant *, std::vector<Function *>> PersonalityFns;
for (Function &F : M) {
if (F.isDeclaration() || !F.hasFnAttribute(Attribute::SanitizeHWAddress))
continue;

if (F.hasPersonalityFn()) {
PersonalityFns[F.getPersonalityFn()->stripPointerCasts()].push_back(&F);
} else if (!F.hasFnAttribute(Attribute::NoUnwind)) {
PersonalityFns[nullptr].push_back(&F);
}
}

if (PersonalityFns.empty())
return;

FunctionCallee HwasanPersonalityWrapper = M.getOrInsertFunction(
"__hwasan_personality_wrapper", Int32Ty, Int32Ty, Int32Ty, Int64Ty,
Int8PtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy);
FunctionCallee UnwindGetGR = M.getOrInsertFunction("_Unwind_GetGR", VoidTy);
FunctionCallee UnwindGetCFA = M.getOrInsertFunction("_Unwind_GetCFA", VoidTy);

for (auto &P : PersonalityFns) {
std::string ThunkName = kHwasanPersonalityThunkName;
if (P.first)
ThunkName += ("." + P.first->getName()).str();
FunctionType *ThunkFnTy = FunctionType::get(
Int32Ty, {Int32Ty, Int32Ty, Int64Ty, Int8PtrTy, Int8PtrTy}, false);
bool IsLocal = P.first && (!isa<GlobalValue>(P.first) ||
cast<GlobalValue>(P.first)->hasLocalLinkage());
auto *ThunkFn = Function::Create(ThunkFnTy,
IsLocal ? GlobalValue::InternalLinkage
: GlobalValue::LinkOnceODRLinkage,
ThunkName, &M);
if (!IsLocal) {
ThunkFn->setVisibility(GlobalValue::HiddenVisibility);
ThunkFn->setComdat(M.getOrInsertComdat(ThunkName));
}

auto *BB = BasicBlock::Create(*C, "entry", ThunkFn);
IRBuilder<> IRB(BB);
CallInst *WrapperCall = IRB.CreateCall(
HwasanPersonalityWrapper,
{ThunkFn->getArg(0), ThunkFn->getArg(1), ThunkFn->getArg(2),
ThunkFn->getArg(3), ThunkFn->getArg(4),
P.first ? IRB.CreateBitCast(P.first, Int8PtrTy)
: Constant::getNullValue(Int8PtrTy),
IRB.CreateBitCast(UnwindGetGR.getCallee(), Int8PtrTy),
IRB.CreateBitCast(UnwindGetCFA.getCallee(), Int8PtrTy)});
WrapperCall->setTailCall();
IRB.CreateRet(WrapperCall);

for (Function *F : P.second)
F->setPersonalityFn(ThunkFn);
}
}

void HWAddressSanitizer::ShadowMapping::init(Triple &TargetTriple) {
Scale = kDefaultShadowScale;
if (ClMappingOffset.getNumOccurrences() > 0) {
Expand Down
10 changes: 6 additions & 4 deletions test/Instrumentation/HWAddressSanitizer/landingpad.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
; RUN: opt < %s -mtriple aarch64-linux-android -hwasan -S | FileCheck %s --check-prefixes=COMMON,ARM
; RUN: opt < %s -mtriple x86_64-linux -hwasan -S | FileCheck %s --check-prefixes=COMMON,X86
; RUN: opt < %s -mtriple aarch64-linux-android29 -hwasan -S | FileCheck %s --check-prefixes=COMMON,LP,ARM
; RUN: opt < %s -mtriple x86_64-linux -hwasan-instrument-landing-pads -hwasan -S | FileCheck %s --check-prefixes=COMMON,LP,X86
; RUN: opt < %s -mtriple aarch64-linux-android30 -hwasan -S | FileCheck %s --check-prefixes=COMMON,NOLP

target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-android"
Expand All @@ -15,8 +16,9 @@ lpad:
%0 = landingpad { i8*, i32 }
catch i8* null

; COMMON-NEXT: %[[X:[^ ]*]] = call i64 @llvm.read_register.i64(metadata ![[META:[^ ]*]])
; COMMON-NEXT: call void @__hwasan_handle_vfork(i64 %[[X]])
; NOLP-NOT: call void @__hwasan_handle_vfork
; LP-NEXT: %[[X:[^ ]*]] = call i64 @llvm.read_register.i64(metadata ![[META:[^ ]*]])
; LP-NEXT: call void @__hwasan_handle_vfork(i64 %[[X]])

%1 = extractvalue { i8*, i32 } %0, 0
%2 = tail call i8* @__cxa_begin_catch(i8* %1)
Expand Down
90 changes: 90 additions & 0 deletions test/Instrumentation/HWAddressSanitizer/personality.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
; RUN: opt < %s -mtriple aarch64-linux-android29 -hwasan -S | FileCheck %s --check-prefix=NOPERS
; RUN: opt < %s -mtriple aarch64-linux-android30 -hwasan -S | FileCheck %s --check-prefix=PERS

; NOPERS: define void @nostack() #{{[0-9]+}} {
; PERS: define void @nostack() #{{[0-9]+}} {
define void @nostack() sanitize_hwaddress {
ret void
}

; NOPERS: define void @stack1() #{{[0-9]+}} {
; PERS: personality {{.*}} @__hwasan_personality_thunk
define void @stack1() sanitize_hwaddress {
%p = alloca i8
call void @sink(i8* %p)
ret void
}


; NOPERS: personality void ()* @global
; PERS: personality {{.*}} @__hwasan_personality_thunk.global
define void @stack2() sanitize_hwaddress personality void ()* @global {
%p = alloca i8
call void @sink(i8* %p)
ret void
}

define internal void @local() {
ret void
}

@local_alias = internal alias void (), void ()* @local

; NOPERS: personality void ()* @local
; PERS: personality {{.*}} @__hwasan_personality_thunk.local
define void @stack3() sanitize_hwaddress personality void ()* @local {
%p = alloca i8
call void @sink(i8* %p)
ret void
}

; NOPERS: personality void ()* @local_alias
; PERS: personality {{.*}} @__hwasan_personality_thunk.local_alias
define void @stack4() sanitize_hwaddress personality void ()* @local_alias {
%p = alloca i8
call void @sink(i8* %p)
ret void
}

; NOPERS: personality void ()* inttoptr (i64 1 to void ()*)
; PERS: personality i32 (i32, i32, i64, i8*, i8*)* @__hwasan_personality_thunk.
define void @stack5() sanitize_hwaddress personality void ()* inttoptr (i64 1 to void ()*) {
%p = alloca i8
call void @sink(i8* %p)
ret void
}

; NOPERS: personality void ()* inttoptr (i64 2 to void ()*)
; PERS: personality i32 (i32, i32, i64, i8*, i8*)* @__hwasan_personality_thunk..1
define void @stack6() sanitize_hwaddress personality void ()* inttoptr (i64 2 to void ()*) {
%p = alloca i8
call void @sink(i8* %p)
ret void
}

declare void @global()
declare void @sink(i8*)

; PERS: define linkonce_odr hidden i32 @__hwasan_personality_thunk(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4) comdat
; PERS: %5 = tail call i32 @__hwasan_personality_wrapper(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4, i8* null, i8* bitcast (void ()* @_Unwind_GetGR to i8*), i8* bitcast (void ()* @_Unwind_GetCFA to i8*))
; PERS: ret i32 %5

; PERS: define linkonce_odr hidden i32 @__hwasan_personality_thunk.global(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4) comdat
; PERS: %5 = tail call i32 @__hwasan_personality_wrapper(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4, i8* bitcast (void ()* @global to i8*), i8* bitcast (void ()* @_Unwind_GetGR to i8*), i8* bitcast (void ()* @_Unwind_GetCFA to i8*))
; PERS: ret i32 %5

; PERS: define internal i32 @__hwasan_personality_thunk.local(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4)
; PERS: %5 = tail call i32 @__hwasan_personality_wrapper(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4, i8* bitcast (void ()* @local to i8*), i8* bitcast (void ()* @_Unwind_GetGR to i8*), i8* bitcast (void ()* @_Unwind_GetCFA to i8*))
; PERS: ret i32 %5

; PERS: define internal i32 @__hwasan_personality_thunk.local_alias(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4)
; PERS: %5 = tail call i32 @__hwasan_personality_wrapper(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4, i8* bitcast (void ()* @local_alias to i8*), i8* bitcast (void ()* @_Unwind_GetGR to i8*), i8* bitcast (void ()* @_Unwind_GetCFA to i8*))
; PERS: ret i32 %5

; PERS: define internal i32 @__hwasan_personality_thunk.(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4) {
; PERS: %5 = tail call i32 @__hwasan_personality_wrapper(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4, i8* inttoptr (i64 1 to i8*), i8* bitcast (void ()* @_Unwind_GetGR to i8*), i8* bitcast (void ()* @_Unwind_GetCFA to i8*))
; PERS: ret i32 %5

; PERS: define internal i32 @__hwasan_personality_thunk..1(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4) {
; PERS: %5 = tail call i32 @__hwasan_personality_wrapper(i32 %0, i32 %1, i64 %2, i8* %3, i8* %4, i8* inttoptr (i64 2 to i8*), i8* bitcast (void ()* @_Unwind_GetGR to i8*), i8* bitcast (void ()* @_Unwind_GetCFA to i8*))
; PERS: ret i32 %5
1 change: 1 addition & 0 deletions utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ source_set("sources") {
"hwasan_allocator.h",
"hwasan_dynamic_shadow.cpp",
"hwasan_dynamic_shadow.h",
"hwasan_exceptions.cpp",
"hwasan_flags.h",
"hwasan_interceptors.cpp",
"hwasan_interceptors_vfork.S",
Expand Down

0 comments on commit 3335b9d

Please sign in to comment.