[asan] Use dynamic shadow on 32-bit Android, try 2.

Summary:
This change reverts r318575 and changes FindDynamicShadowStart() to
keep the memory range it found mapped PROT_NONE to make sure it is
not reused. We also skip MemoryRangeIsAvailable() check, because it
is (a) unnecessary, and (b) would fail anyway.

Reviewers: pcc, vitalybuka, kcc

Subscribers: srhines, kubamracek, mgorny, llvm-commits, hiraditya

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

llvm-svn: 318666
This commit is contained in:
Evgeniy Stepanov 2017-11-20 17:41:57 +00:00
parent a07077f9dd
commit 50c8d56daa
2 changed files with 110 additions and 11 deletions

View File

@ -137,8 +137,8 @@ static const char *const kAsanUnregisterElfGlobalsName =
static const char *const kAsanPoisonGlobalsName = "__asan_before_dynamic_init";
static const char *const kAsanUnpoisonGlobalsName = "__asan_after_dynamic_init";
static const char *const kAsanInitName = "__asan_init";
static const char *const kAsanVersionCheckName =
"__asan_version_mismatch_check_v8";
static const char *const kAsanVersionCheckNamePrefix =
"__asan_version_mismatch_check_v";
static const char *const kAsanPtrCmp = "__sanitizer_ptr_cmp";
static const char *const kAsanPtrSub = "__sanitizer_ptr_sub";
static const char *const kAsanHandleNoReturnName = "__asan_handle_no_return";
@ -208,6 +208,18 @@ static cl::opt<bool> ClForceDynamicShadow(
cl::desc("Load shadow address into a local variable for each function"),
cl::Hidden, cl::init(false));
static cl::opt<bool>
ClWithIfunc("asan-with-ifunc",
cl::desc("Access dynamic shadow through an ifunc global on "
"platforms that support this"),
cl::Hidden, cl::init(true));
static cl::opt<bool> ClWithIfuncSuppressRemat(
"asan-with-ifunc-suppress-remat",
cl::desc("Suppress rematerialization of dynamic shadow address by passing "
"it through inline asm in prologue."),
cl::Hidden, cl::init(true));
// This flag limits the number of instructions to be instrumented
// in any given BB. Normally, this should be set to unlimited (INT_MAX),
// but due to http://llvm.org/bugs/show_bug.cgi?id=12652 we temporary
@ -448,10 +460,14 @@ private:
/// This struct defines the shadow mapping using the rule:
/// shadow = (mem >> Scale) ADD-or-OR Offset.
/// If InGlobal is true, then
/// extern char __asan_shadow[];
/// shadow = (mem >> Scale) + &__asan_shadow
struct ShadowMapping {
int Scale;
uint64_t Offset;
bool OrShadowOffset;
bool InGlobal;
};
} // end anonymous namespace
@ -473,6 +489,7 @@ static ShadowMapping getShadowMapping(Triple &TargetTriple, int LongSize,
TargetTriple.getArch() == Triple::mipsel;
bool IsMIPS64 = TargetTriple.getArch() == Triple::mips64 ||
TargetTriple.getArch() == Triple::mips64el;
bool IsArmOrThumb = TargetTriple.isARM() || TargetTriple.isThumb();
bool IsAArch64 = TargetTriple.getArch() == Triple::aarch64;
bool IsWindows = TargetTriple.isOSWindows();
bool IsFuchsia = TargetTriple.isOSFuchsia();
@ -485,10 +502,8 @@ static ShadowMapping getShadowMapping(Triple &TargetTriple, int LongSize,
}
if (LongSize == 32) {
// Android is always PIE, which means that the beginning of the address
// space is always available.
if (IsAndroid)
Mapping.Offset = 0;
Mapping.Offset = kDynamicShadowSentinel;
else if (IsMIPS32)
Mapping.Offset = kMIPS32_ShadowOffset32;
else if (IsFreeBSD)
@ -552,6 +567,9 @@ static ShadowMapping getShadowMapping(Triple &TargetTriple, int LongSize,
Mapping.OrShadowOffset = !IsAArch64 && !IsPPC64 && !IsSystemZ && !IsPS4CPU &&
!(Mapping.Offset & (Mapping.Offset - 1)) &&
Mapping.Offset != kDynamicShadowSentinel;
bool IsAndroidWithIfuncSupport =
IsAndroid && !TargetTriple.isAndroidVersionLT(21);
Mapping.InGlobal = ClWithIfunc && IsAndroidWithIfuncSupport && IsArmOrThumb;
return Mapping;
}
@ -674,6 +692,7 @@ private:
DominatorTree *DT;
Function *AsanHandleNoReturnFunc;
Function *AsanPtrCmpFunction, *AsanPtrSubFunction;
Constant *AsanShadowGlobal;
// These arrays is indexed by AccessIsWrite, Experiment and log2(AccessSize).
Function *AsanErrorCallback[2][2][kNumberOfAccessSizes];
@ -746,6 +765,7 @@ private:
size_t MinRedzoneSizeForGlobal() const {
return RedzoneSizeForScale(Mapping.Scale);
}
int GetAsanVersion(const Module &M) const;
GlobalsMetadata GlobalsMD;
bool CompileKernel;
@ -978,8 +998,9 @@ struct FunctionStackPoisoner : public InstVisitor<FunctionStackPoisoner> {
void visitCallSite(CallSite CS) {
Instruction *I = CS.getInstruction();
if (CallInst *CI = dyn_cast<CallInst>(I)) {
HasNonEmptyInlineAsm |=
CI->isInlineAsm() && !CI->isIdenticalTo(EmptyInlineAsm.get());
HasNonEmptyInlineAsm |= CI->isInlineAsm() &&
!CI->isIdenticalTo(EmptyInlineAsm.get()) &&
I != ASan.LocalDynamicShadow;
HasReturnsTwiceCall |= CI->canReturnTwice();
}
}
@ -2160,6 +2181,16 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool
return true;
}
int AddressSanitizerModule::GetAsanVersion(const Module &M) const {
int LongSize = M.getDataLayout().getPointerSizeInBits();
bool isAndroid = Triple(M.getTargetTriple()).isAndroid();
int Version = 8;
// 32-bit Android is one version ahead because of the switch to dynamic
// shadow.
Version += (LongSize == 32 && isAndroid);
return Version;
}
bool AddressSanitizerModule::runOnModule(Module &M) {
C = &(M.getContext());
int LongSize = M.getDataLayout().getPointerSizeInBits();
@ -2173,9 +2204,11 @@ bool AddressSanitizerModule::runOnModule(Module &M) {
// Create a module constructor. A destructor is created lazily because not all
// platforms, and not all modules need it.
std::string VersionCheckName =
kAsanVersionCheckNamePrefix + std::to_string(GetAsanVersion(M));
std::tie(AsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
M, kAsanModuleCtorName, kAsanInitName, /*InitArgTypes=*/{},
/*InitArgs=*/{}, kAsanVersionCheckName);
/*InitArgs=*/{}, VersionCheckName);
bool CtorComdat = true;
bool Changed = false;
@ -2274,6 +2307,9 @@ void AddressSanitizer::initializeCallbacks(Module &M) {
EmptyAsm = InlineAsm::get(FunctionType::get(IRB.getVoidTy(), false),
StringRef(""), StringRef(""),
/*hasSideEffects=*/true);
if (Mapping.InGlobal)
AsanShadowGlobal = M.getOrInsertGlobal("__asan_shadow",
ArrayType::get(IRB.getInt8Ty(), 0));
}
// virtual
@ -2319,9 +2355,25 @@ void AddressSanitizer::maybeInsertDynamicShadowAtFunctionEntry(Function &F) {
return;
IRBuilder<> IRB(&F.front().front());
Value *GlobalDynamicAddress = F.getParent()->getOrInsertGlobal(
kAsanShadowMemoryDynamicAddress, IntptrTy);
LocalDynamicShadow = IRB.CreateLoad(GlobalDynamicAddress);
if (Mapping.InGlobal) {
if (ClWithIfuncSuppressRemat) {
// An empty inline asm with input reg == output reg.
// An opaque pointer-to-int cast, basically.
InlineAsm *Asm = InlineAsm::get(
FunctionType::get(IntptrTy, {AsanShadowGlobal->getType()}, false),
StringRef(""), StringRef("=r,0"),
/*hasSideEffects=*/false);
LocalDynamicShadow =
IRB.CreateCall(Asm, {AsanShadowGlobal}, ".asan.shadow");
} else {
LocalDynamicShadow =
IRB.CreatePointerCast(AsanShadowGlobal, IntptrTy, ".asan.shadow");
}
} else {
Value *GlobalDynamicAddress = F.getParent()->getOrInsertGlobal(
kAsanShadowMemoryDynamicAddress, IntptrTy);
LocalDynamicShadow = IRB.CreateLoad(GlobalDynamicAddress);
}
}
void AddressSanitizer::markEscapedLocalAllocas(Function &F) {

View File

@ -0,0 +1,47 @@
; Test -asan-with-ifunc flag.
;
; RUN: opt -asan -asan-module -S -asan-with-ifunc=0 < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK-NOIFUNC
; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=0 < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK-IFUNC
; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=1 < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK-IFUNC-NOREMAT
; Pre-Lollipop Android does not support ifunc.
; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=0 -mtriple=armv7-linux-android20 < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK-NOIFUNC
; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=0 -mtriple=armv7-linux-android < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK-NOIFUNC
; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=0 -mtriple=armv7-linux-android21 < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK-IFUNC
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv7--linux-android22"
; CHECK-IFUNC: @__asan_shadow = external global [0 x i8]
; CHECK-NOIFUNC: @__asan_shadow_memory_dynamic_address = external global i32
define i32 @test_load(i32* %a) sanitize_address {
; First instrumentation in the function must be to load the dynamic shadow
; address into a local variable.
; CHECK-LABEL: @test_load
; CHECK: entry:
; CHECK-IFUNC-NEXT: %[[A:[^ ]*]] = ptrtoint i32* %a to i32
; CHECK-IFUNC-NEXT: %[[B:[^ ]*]] = lshr i32 %[[A]], 3
; CHECK-IFUNC-NEXT: %[[C:[^ ]*]] = add i32 %[[B]], ptrtoint ([0 x i8]* @__asan_shadow to i32)
; CHECK-IFUNC-NOREMAT-NEXT: %[[S:[^ ]*]] = call i32 asm "", "=r,0"([0 x i8]* @__asan_shadow)
; CHECK-IFUNC-NOREMAT-NEXT: %[[A:[^ ]*]] = ptrtoint i32* %a to i32
; CHECK-IFUNC-NOREMAT-NEXT: %[[B:[^ ]*]] = lshr i32 %[[A]], 3
; CHECK-IFUNC-NOREMAT-NEXT: %[[C:[^ ]*]] = add i32 %[[B]], %[[S]]
; CHECK-NOIFUNC-NEXT: %[[SHADOW:[^ ]*]] = load i32, i32* @__asan_shadow_memory_dynamic_address
; CHECK-NOIFUNC-NEXT: %[[A:[^ ]*]] = ptrtoint i32* %a to i32
; CHECK-NOIFUNC-NEXT: %[[B:[^ ]*]] = lshr i32 %[[A]], 3
; CHECK-NOIFUNC-NEXT: %[[C:[^ ]*]] = add i32 %[[B]], %[[SHADOW]]
entry:
%x = load i32, i32* %a, align 4
ret i32 %x
}