From 7f281b2c06b19a64a2d630df02ccbf23526e6728 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Thu, 16 May 2019 23:54:41 +0000 Subject: [PATCH] HWASan exception support. Summary: Adds a call to __hwasan_handle_vfork(SP) at each landingpad entry. Reusing __hwasan_handle_vfork instead of introducing a new runtime call in order to be ABI-compatible with old runtime library. Reviewers: pcc Subscribers: kubamracek, hiraditya, #sanitizers, llvm-commits Tags: #sanitizers, #llvm Differential Revision: https://reviews.llvm.org/D61968 llvm-svn: 360959 --- .../test/hwasan/TestCases/try-catch.cc | 60 +++++++++++++++++++ .../Instrumentation/HWAddressSanitizer.cpp | 37 +++++++++++- .../HWAddressSanitizer/landingpad.ll | 37 ++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 compiler-rt/test/hwasan/TestCases/try-catch.cc create mode 100644 llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll diff --git a/compiler-rt/test/hwasan/TestCases/try-catch.cc b/compiler-rt/test/hwasan/TestCases/try-catch.cc new file mode 100644 index 000000000000..68b9011a2386 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/try-catch.cc @@ -0,0 +1,60 @@ +// RUN: %clangxx_hwasan %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=GOOD +// RUN: %clangxx_hwasan %s -mllvm -hwasan-instrument-landing-pads=0 -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=BAD + +#include +#include + +static void optimization_barrier(void* arg) { + asm volatile("" : : "r"(arg) : "memory"); +} + +__attribute__((noinline)) +void h() { + char x[1000]; + optimization_barrier(x); + throw std::runtime_error("hello"); +} + +__attribute__((noinline)) +void g() { + char x[1000]; + optimization_barrier(x); + h(); + optimization_barrier(x); +} + +__attribute__((noinline)) +void hwasan_read(char *p, int size) { + char volatile sink; + for (int i = 0; i < size; ++i) + sink = p[i]; +} + +__attribute__((noinline, no_sanitize("hwaddress"))) void after_catch() { + char x[10000]; + hwasan_read(&x[0], sizeof(x)); +} + + +__attribute__((noinline)) +void f() { + char x[1000]; + try { + // Put two tagged frames on the stack, throw an exception from the deepest one. + g(); + } catch (const std::runtime_error &e) { + // Put an untagged frame on stack, check that it is indeed untagged. + // This relies on exception support zeroing out stack tags. + // BAD: tag-mismatch + after_catch(); + // Check that an in-scope stack allocation is still tagged. + // This relies on exception support not zeroing too much. + hwasan_read(&x[0], sizeof(x)); + // GOOD: hello + printf("%s\n", e.what()); + } +} + +int main() { + f(); +} diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp index 56db746565bb..abbe5801de1a 100644 --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -157,6 +157,11 @@ static cl::opt cl::desc("instrument memory intrinsics"), cl::Hidden, cl::init(true)); +static cl::opt + ClInstrumentLandingPads("hwasan-instrument-landing-pads", + cl::desc("instrument landing pads"), cl::Hidden, + cl::init(true)); + static cl::opt ClInlineAllChecks("hwasan-inline-all-checks", cl::desc("inline all checks"), cl::Hidden, cl::init(false)); @@ -202,6 +207,7 @@ public: Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong); bool instrumentStack(SmallVectorImpl &Allocas, SmallVectorImpl &RetVec, Value *StackTag); + bool instrumentLandingPads(SmallVectorImpl &RetVec); Value *getNextTagWithCall(IRBuilder<> &IRB); Value *getStackBaseTag(IRBuilder<> &IRB); Value *getAllocaTag(IRBuilder<> &IRB, Value *StackTag, AllocaInst *AI, @@ -216,6 +222,7 @@ private: std::string CurModuleUniqueId; Triple TargetTriple; FunctionCallee HWAsanMemmove, HWAsanMemcpy, HWAsanMemset; + FunctionCallee HWAsanHandleVfork; // 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. @@ -440,6 +447,9 @@ void HWAddressSanitizer::initializeCallbacks(Module &M) { IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy); + HWAsanHandleVfork = + M.getOrInsertFunction("__hwasan_handle_vfork", IRB.getVoidTy(), IntptrTy); + HwasanThreadEnterFunc = M.getOrInsertFunction("__hwasan_thread_enter", IRB.getVoidTy()); } @@ -955,6 +965,23 @@ Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB, return ShadowBase; } +bool HWAddressSanitizer::instrumentLandingPads( + SmallVectorImpl &LandingPadVec) { + Module *M = LandingPadVec[0]->getModule(); + Function *ReadRegister = + Intrinsic::getDeclaration(M, Intrinsic::read_register, IntptrTy); + const char *RegName = + (TargetTriple.getArch() == Triple::x86_64) ? "rsp" : "sp"; + MDNode *MD = MDNode::get(*C, {MDString::get(*C, RegName)}); + Value *Args[] = {MetadataAsValue::get(*C, MD)}; + + for (auto *LP : LandingPadVec) { + IRBuilder<> IRB(LP->getNextNode()); + IRB.CreateCall(HWAsanHandleVfork, {IRB.CreateCall(ReadRegister, Args)}); + } + return true; +} + bool HWAddressSanitizer::instrumentStack( SmallVectorImpl &Allocas, SmallVectorImpl &RetVec, Value *StackTag) { @@ -1023,6 +1050,7 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) { SmallVector ToInstrument; SmallVector AllocasToInstrument; SmallVector RetVec; + SmallVector LandingPadVec; for (auto &BB : F) { for (auto &Inst : BB) { if (ClInstrumentStack) @@ -1041,6 +1069,9 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) { isa(Inst)) RetVec.push_back(&Inst); + if (ClInstrumentLandingPads && isa(Inst)) + LandingPadVec.push_back(&Inst); + Value *MaybeMask = nullptr; bool IsWrite; unsigned Alignment; @@ -1052,13 +1083,17 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) { } } + initializeCallbacks(*F.getParent()); + + if (!LandingPadVec.empty()) + instrumentLandingPads(LandingPadVec); + if (AllocasToInstrument.empty() && ToInstrument.empty()) return false; if (ClCreateFrameDescriptions && !AllocasToInstrument.empty()) createFrameGlobal(F, createFrameString(AllocasToInstrument)); - initializeCallbacks(*F.getParent()); assert(!LocalDynamicShadow); diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll b/llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll new file mode 100644 index 000000000000..15cc0bc95d9e --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll @@ -0,0 +1,37 @@ +; 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 + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-android" + +define i32 @f() local_unnamed_addr sanitize_hwaddress personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +entry: + invoke void @g() + to label %return unwind label %lpad + +lpad: + ; COMMON: landingpad { i8*, i32 } + ; COMMON-NEXT: catch i8* null + %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]]) + + %1 = extractvalue { i8*, i32 } %0, 0 + %2 = tail call i8* @__cxa_begin_catch(i8* %1) + tail call void @__cxa_end_catch() + br label %return +return: + %retval.0 = phi i32 [ 1, %lpad ], [ 0, %entry ] + ret i32 %retval.0 +} + +declare void @g() local_unnamed_addr + +declare i32 @__gxx_personality_v0(...) +declare i8* @__cxa_begin_catch(i8*) local_unnamed_addr +declare void @__cxa_end_catch() local_unnamed_addr + +; ARM: ![[META]] = !{!"sp"} +; X86: ![[META]] = !{!"rsp"}