//===----------------------------------------------------------------------===//
#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"
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";
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"),
void instrumentGlobal(GlobalVariable *GV, uint8_t Tag);
void instrumentGlobals();
+ void instrumentPersonalityFunctions();
+
private:
LLVMContext *C;
Module &M;
};
ShadowMapping Mapping;
+ Type *VoidTy = Type::getVoidTy(M.getContext());
Type *IntptrTy;
Type *Int8PtrTy;
Type *Int8Ty;
bool CompileKernel;
bool Recover;
+ bool InstrumentLandingPads;
Function *HwasanCtorFunction;
});
// 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()) {
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;
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;
}
}
+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) {
--- /dev/null
+; 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