diff --git a/compiler-rt/lib/hwasan/CMakeLists.txt b/compiler-rt/lib/hwasan/CMakeLists.txt index c094b273e654..c976f994651b 100644 --- a/compiler-rt/lib/hwasan/CMakeLists.txt +++ b/compiler-rt/lib/hwasan/CMakeLists.txt @@ -5,6 +5,7 @@ set(HWASAN_RTL_SOURCES hwasan.cpp hwasan_allocator.cpp hwasan_dynamic_shadow.cpp + hwasan_exceptions.cpp hwasan_interceptors.cpp hwasan_interceptors_vfork.S hwasan_linux.cpp diff --git a/compiler-rt/lib/hwasan/hwasan_exceptions.cpp b/compiler-rt/lib/hwasan/hwasan_exceptions.cpp new file mode 100644 index 000000000000..57a1438064cd --- /dev/null +++ b/compiler-rt/lib/hwasan/hwasan_exceptions.cpp @@ -0,0 +1,64 @@ +//===-- hwasan_exceptions.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of HWAddressSanitizer. +// +// HWAddressSanitizer runtime. +//===----------------------------------------------------------------------===// + +#include "hwasan_poisoning.h" +#include "sanitizer_common/sanitizer_common.h" + +#include + +using namespace __hwasan; +using namespace __sanitizer; + +typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions, + uint64_t exception_class, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context); + +// Pointers to the _Unwind_GetGR and _Unwind_GetCFA functions are passed in +// instead of being called directly. This is to handle cases where the unwinder +// is statically linked and the sanitizer runtime and the program are linked +// against different unwinders. The _Unwind_Context data structure is opaque so +// it may be incompatible between unwinders. +typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index); +typedef _Unwind_Word GetCFAFn(_Unwind_Context* context); + +extern "C" _Unwind_Reason_Code __hwasan_personality_wrapper( + int version, _Unwind_Action actions, uint64_t exception_class, + _Unwind_Exception* unwind_exception, _Unwind_Context* context, + PersonalityFn* real_personality, GetGRFn* get_gr, GetCFAFn* get_cfa) { + _Unwind_Reason_Code rc; + if (real_personality) + rc = real_personality(version, actions, exception_class, unwind_exception, + context); + else + rc = _URC_CONTINUE_UNWIND; + + // We only untag frames without a landing pad because landing pads are + // responsible for untagging the stack themselves if they resume. + // + // Here we assume that the frame record appears after any locals. This is not + // required by AAPCS but is a requirement for HWASAN instrumented functions. + if ((actions & _UA_CLEANUP_PHASE) && rc == _URC_CONTINUE_UNWIND) { +#if defined(__x86_64__) + uptr fp = get_gr(context, 6); // rbp +#elif defined(__aarch64__) + uptr fp = get_gr(context, 29); // x29 +#else +#error Unsupported architecture +#endif + uptr sp = get_cfa(context); + TagMemory(sp, fp - sp, 0); + } + + return rc; +} diff --git a/compiler-rt/test/hwasan/TestCases/try-catch.cpp b/compiler-rt/test/hwasan/TestCases/try-catch.cpp index df1f93d2baee..ce1a92b34849 100644 --- a/compiler-rt/test/hwasan/TestCases/try-catch.cpp +++ b/compiler-rt/test/hwasan/TestCases/try-catch.cpp @@ -1,5 +1,7 @@ // 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 +// RUN: %clangxx_hwasan -DNO_SANITIZE_F %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=GOOD +// RUN: %clangxx_hwasan_oldrt %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=GOOD +// RUN: %clangxx_hwasan_oldrt %s -mllvm -hwasan-instrument-landing-pads=0 -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=BAD // C++ tests on x86_64 require instrumented libc++/libstdc++. // REQUIRES: aarch64-target-arch @@ -40,6 +42,9 @@ __attribute__((noinline, no_sanitize("hwaddress"))) void after_catch() { __attribute__((noinline)) +#ifdef NO_SANITIZE_F +__attribute__((no_sanitize("hwaddress"))) +#endif void f() { char x[1000]; try { diff --git a/compiler-rt/test/hwasan/lit.cfg.py b/compiler-rt/test/hwasan/lit.cfg.py index 06a860d18509..d2e71ba6d57b 100644 --- a/compiler-rt/test/hwasan/lit.cfg.py +++ b/compiler-rt/test/hwasan/lit.cfg.py @@ -11,13 +11,17 @@ config.test_source_root = os.path.dirname(__file__) # 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 = clang_cflags + ["-fsanitize=hwaddress", "-mllvm", "-hwasan-globals", "-fuse-ld=lld"] +clang_hwasan_oldrt_cflags = clang_cflags + ["-fsanitize=hwaddress", "-fuse-ld=lld"] 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_oldrt_cflags += ["-mcmodel=large"] +clang_hwasan_cflags = clang_hwasan_oldrt_cflags + ["-mllvm", "-hwasan-globals", + "-mllvm", "-hwasan-instrument-landing-pads=0", + "-mllvm", "-hwasan-instrument-personality-functions"] clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags +clang_hwasan_oldrt_cxxflags = config.cxx_mode_flags + clang_hwasan_oldrt_cflags def build_invocation(compile_flags): return " " + " ".join([config.clang] + compile_flags) + " " @@ -25,6 +29,7 @@ def build_invocation(compile_flags): config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) config.substitutions.append( ("%clang_hwasan ", build_invocation(clang_hwasan_cflags)) ) config.substitutions.append( ("%clangxx_hwasan ", build_invocation(clang_hwasan_cxxflags)) ) +config.substitutions.append( ("%clangxx_hwasan_oldrt ", build_invocation(clang_hwasan_oldrt_cxxflags)) ) config.substitutions.append( ("%compiler_rt_libdir", config.compiler_rt_libdir) ) default_hwasan_opts_str = ':'.join(['disable_allocator_tagging=1', 'random_tags=0'] + config.default_sanitizer_opts) diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp index 163ae3ec62f9..874361d47556 100644 --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -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" @@ -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"; @@ -160,8 +163,12 @@ static cl::opt static cl::opt 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 ClInstrumentPersonalityFunctions( + "hwasan-instrument-personality-functions", + cl::desc("instrument personality functions"), cl::Hidden, cl::init(false)); static cl::opt ClInlineAllChecks("hwasan-inline-all-checks", cl::desc("inline all checks"), @@ -224,6 +231,8 @@ public: void instrumentGlobal(GlobalVariable *GV, uint8_t Tag); void instrumentGlobals(); + void instrumentPersonalityFunctions(); + private: LLVMContext *C; Module &M; @@ -250,6 +259,7 @@ private: }; ShadowMapping Mapping; + Type *VoidTy = Type::getVoidTy(M.getContext()); Type *IntptrTy; Type *Int8PtrTy; Type *Int8Ty; @@ -258,6 +268,7 @@ private: bool CompileKernel; bool Recover; + bool InstrumentLandingPads; Function *HwasanCtorFunction; @@ -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()) { @@ -1092,7 +1116,7 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) { if (auto *Alloca = dyn_cast_or_null(DDI->getAddress())) AllocaDeclareMap[Alloca].push_back(DDI); - if (ClInstrumentLandingPads && isa(Inst)) + if (InstrumentLandingPads && isa(Inst)) LandingPadVec.push_back(&Inst); Value *MaybeMask = nullptr; @@ -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; @@ -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> 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(P.first) || + cast(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) { diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll b/llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll index 15cc0bc95d9e..bb6eb02b4696 100644 --- a/llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll +++ b/llvm/test/Instrumentation/HWAddressSanitizer/landingpad.ll @@ -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" @@ -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) diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/personality.ll b/llvm/test/Instrumentation/HWAddressSanitizer/personality.ll new file mode 100644 index 000000000000..ad969e393326 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/personality.ll @@ -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 diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn b/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn index f29d2455a19d..c2a4817c3fac 100644 --- a/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn +++ b/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn @@ -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",