mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-23 11:49:50 +00:00
Android support for SafeStack.
Add two new ways of accessing the unsafe stack pointer: * At a fixed offset from the thread TLS base. This is very similar to StackProtector cookies, but we plan to extend it to other backends (ARM in particular) soon. Bionic-side implementation here: https://android-review.googlesource.com/170988. * Via a function call, as a fallback for platforms that provide neither a fixed TLS slot, nor a reasonable TLS implementation (i.e. not emutls). This is a re-commit of a change in r248357 that was reverted in r248358. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@248405 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
5e2a635f0e
commit
d4052cf84c
@ -995,6 +995,14 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Return true if the target stores SafeStack pointer at a fixed offset in
|
||||
/// some non-standard address space, and populates the address space and
|
||||
/// offset as appropriate.
|
||||
virtual bool getSafeStackPointerLocation(unsigned & /*AddressSpace*/,
|
||||
unsigned & /*Offset*/) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if a cast between SrcAS and DestAS is a noop.
|
||||
virtual bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const {
|
||||
return false;
|
||||
|
@ -34,6 +34,8 @@ inline void *getDFSanRetValTLSPtrForJIT() {
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class TargetMachine;
|
||||
|
||||
/// Instrumentation passes often insert conditional checks into entry blocks.
|
||||
/// Call this function before splitting the entry block to move instructions
|
||||
/// that must remain in the entry block up before the split point. Static
|
||||
@ -143,7 +145,7 @@ FunctionPass *createBoundsCheckingPass();
|
||||
|
||||
/// \brief This pass splits the stack into a safe stack and an unsafe stack to
|
||||
/// protect against stack-based overflow vulnerabilities.
|
||||
FunctionPass *createSafeStackPass();
|
||||
FunctionPass *createSafeStackPass(const TargetMachine *TM = nullptr);
|
||||
|
||||
} // End llvm namespace
|
||||
|
||||
|
@ -466,7 +466,7 @@ void TargetPassConfig::addISelPrepare() {
|
||||
|
||||
// Add both the safe stack and the stack protection passes: each of them will
|
||||
// only protect functions that have corresponding attributes.
|
||||
addPass(createSafeStackPass());
|
||||
addPass(createSafeStackPass(TM));
|
||||
addPass(createStackProtectorPass(TM));
|
||||
|
||||
if (PrintISelInput)
|
||||
|
@ -2079,6 +2079,29 @@ bool X86TargetLowering::getStackCookieLocation(unsigned &AddressSpace,
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Android provides a fixed TLS slot for the SafeStack pointer.
|
||||
/// See the definition of TLS_SLOT_SAFESTACK in
|
||||
/// https://android.googlesource.com/platform/bionic/+/master/libc/private/bionic_tls.h
|
||||
bool X86TargetLowering::getSafeStackPointerLocation(unsigned &AddressSpace,
|
||||
unsigned &Offset) const {
|
||||
if (!Subtarget->isTargetAndroid())
|
||||
return false;
|
||||
|
||||
if (Subtarget->is64Bit()) {
|
||||
// %fs:0x48, unless we're using a Kernel code model, in which case it's %gs:
|
||||
Offset = 0x48;
|
||||
if (getTargetMachine().getCodeModel() == CodeModel::Kernel)
|
||||
AddressSpace = 256;
|
||||
else
|
||||
AddressSpace = 257;
|
||||
} else {
|
||||
// %gs:0x24 on i386
|
||||
Offset = 0x24;
|
||||
AddressSpace = 256;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool X86TargetLowering::isNoopAddrSpaceCast(unsigned SrcAS,
|
||||
unsigned DestAS) const {
|
||||
assert(SrcAS != DestAS && "Expected different address spaces!");
|
||||
|
@ -891,6 +891,12 @@ namespace llvm {
|
||||
bool getStackCookieLocation(unsigned &AddressSpace,
|
||||
unsigned &Offset) const override;
|
||||
|
||||
/// Return true if the target stores SafeStack pointer at a fixed offset in
|
||||
/// some non-standard address space, and populates the address space and
|
||||
/// offset as appropriate.
|
||||
bool getSafeStackPointerLocation(unsigned &AddressSpace,
|
||||
unsigned &Offset) const override;
|
||||
|
||||
SDValue BuildFILD(SDValue Op, EVT SrcVT, SDValue Chain, SDValue StackSlot,
|
||||
SelectionDAG &DAG) const;
|
||||
|
||||
|
@ -394,6 +394,9 @@ public:
|
||||
bool isTargetMachO() const { return TargetTriple.isOSBinFormatMachO(); }
|
||||
|
||||
bool isTargetLinux() const { return TargetTriple.isOSLinux(); }
|
||||
bool isTargetAndroid() const {
|
||||
return TargetTriple.getEnvironment() == Triple::Android;
|
||||
}
|
||||
bool isTargetNaCl() const { return TargetTriple.isOSNaCl(); }
|
||||
bool isTargetNaCl32() const { return isTargetNaCl() && !is64Bit(); }
|
||||
bool isTargetNaCl64() const { return isTargetNaCl() && is64Bit(); }
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
@ -36,6 +37,8 @@
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/raw_os_ostream.h"
|
||||
#include "llvm/Target/TargetLowering.h"
|
||||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||
|
||||
@ -43,6 +46,9 @@ using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "safestack"
|
||||
|
||||
static const char *const kUnsafeStackPtrVar = "__safestack_unsafe_stack_ptr";
|
||||
static const char *const kUnsafeStackPtrAddrFn = "__safestack_pointer_address";
|
||||
|
||||
namespace llvm {
|
||||
|
||||
STATISTIC(NumFunctions, "Total number of functions");
|
||||
@ -157,6 +163,8 @@ bool IsSafeStackAlloca(const AllocaInst *AI) {
|
||||
/// (as determined statically), and the unsafe stack, which contains all
|
||||
/// local variables that are accessed in unsafe ways.
|
||||
class SafeStack : public FunctionPass {
|
||||
const TargetMachine *TM;
|
||||
const TargetLoweringBase *TLI;
|
||||
const DataLayout *DL;
|
||||
|
||||
Type *StackPtrTy;
|
||||
@ -164,7 +172,7 @@ class SafeStack : public FunctionPass {
|
||||
Type *Int32Ty;
|
||||
Type *Int8Ty;
|
||||
|
||||
Constant *UnsafeStackPtr = nullptr;
|
||||
Value *UnsafeStackPtr = nullptr;
|
||||
|
||||
/// Unsafe stack alignment. Each stack frame must ensure that the stack is
|
||||
/// aligned to this value. We need to re-align the unsafe stack if the
|
||||
@ -176,7 +184,7 @@ class SafeStack : public FunctionPass {
|
||||
|
||||
/// \brief Build a constant representing a pointer to the unsafe stack
|
||||
/// pointer.
|
||||
Constant *getOrCreateUnsafeStackPtr(Module &M);
|
||||
Value *getOrCreateUnsafeStackPtr(IRBuilder<> &IRB, Function &F);
|
||||
|
||||
/// \brief Find all static allocas, dynamic allocas, return instructions and
|
||||
/// stack restore points (exception unwind blocks and setjmp calls) in the
|
||||
@ -192,7 +200,7 @@ class SafeStack : public FunctionPass {
|
||||
///
|
||||
/// \returns A pointer to the top of the unsafe stack after all unsafe static
|
||||
/// allocas are allocated.
|
||||
Value *moveStaticAllocasToUnsafeStack(Function &F,
|
||||
Value *moveStaticAllocasToUnsafeStack(IRBuilder<> &IRB, Function &F,
|
||||
ArrayRef<AllocaInst *> StaticAllocas,
|
||||
ArrayRef<ReturnInst *> Returns);
|
||||
|
||||
@ -215,9 +223,11 @@ class SafeStack : public FunctionPass {
|
||||
|
||||
public:
|
||||
static char ID; // Pass identification, replacement for typeid.
|
||||
SafeStack() : FunctionPass(ID), DL(nullptr) {
|
||||
SafeStack(const TargetMachine *TM)
|
||||
: FunctionPass(ID), TM(TM), TLI(nullptr), DL(nullptr) {
|
||||
initializeSafeStackPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
SafeStack() : SafeStack(nullptr) {}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<AAResultsWrapperPass>();
|
||||
@ -237,35 +247,52 @@ public:
|
||||
bool runOnFunction(Function &F) override;
|
||||
}; // class SafeStack
|
||||
|
||||
Constant *SafeStack::getOrCreateUnsafeStackPtr(Module &M) {
|
||||
// The unsafe stack pointer is stored in a global variable with a magic name.
|
||||
const char *kUnsafeStackPtrVar = "__safestack_unsafe_stack_ptr";
|
||||
Value *SafeStack::getOrCreateUnsafeStackPtr(IRBuilder<> &IRB, Function &F) {
|
||||
Module &M = *F.getParent();
|
||||
Triple TargetTriple(M.getTargetTriple());
|
||||
|
||||
auto UnsafeStackPtr =
|
||||
dyn_cast_or_null<GlobalVariable>(M.getNamedValue(kUnsafeStackPtrVar));
|
||||
|
||||
if (!UnsafeStackPtr) {
|
||||
// The global variable is not defined yet, define it ourselves.
|
||||
// We use the initial-exec TLS model because we do not support the variable
|
||||
// living anywhere other than in the main executable.
|
||||
UnsafeStackPtr = new GlobalVariable(
|
||||
/*Module=*/M, /*Type=*/StackPtrTy,
|
||||
/*isConstant=*/false, /*Linkage=*/GlobalValue::ExternalLinkage,
|
||||
/*Initializer=*/0, /*Name=*/kUnsafeStackPtrVar,
|
||||
/*InsertBefore=*/nullptr,
|
||||
/*ThreadLocalMode=*/GlobalValue::InitialExecTLSModel);
|
||||
} else {
|
||||
// The variable exists, check its type and attributes.
|
||||
if (UnsafeStackPtr->getValueType() != StackPtrTy) {
|
||||
report_fatal_error(Twine(kUnsafeStackPtrVar) + " must have void* type");
|
||||
}
|
||||
|
||||
if (!UnsafeStackPtr->isThreadLocal()) {
|
||||
report_fatal_error(Twine(kUnsafeStackPtrVar) + " must be thread-local");
|
||||
}
|
||||
unsigned Offset;
|
||||
unsigned AddressSpace;
|
||||
// Check if the target keeps the unsafe stack pointer at a fixed offset.
|
||||
if (TLI && TLI->getSafeStackPointerLocation(Offset, AddressSpace)) {
|
||||
Constant *OffsetVal =
|
||||
ConstantInt::get(Type::getInt32Ty(F.getContext()), Offset);
|
||||
return ConstantExpr::getIntToPtr(OffsetVal,
|
||||
StackPtrTy->getPointerTo(AddressSpace));
|
||||
}
|
||||
|
||||
return UnsafeStackPtr;
|
||||
// Android provides a libc function that returns the stack pointer address.
|
||||
if (TargetTriple.getEnvironment() == llvm::Triple::Android) {
|
||||
Value *Fn = M.getOrInsertFunction(kUnsafeStackPtrAddrFn,
|
||||
StackPtrTy->getPointerTo(0), nullptr);
|
||||
return IRB.CreateCall(Fn);
|
||||
} else {
|
||||
// Otherwise, declare a thread-local variable with a magic name.
|
||||
auto UnsafeStackPtr =
|
||||
dyn_cast_or_null<GlobalVariable>(M.getNamedValue(kUnsafeStackPtrVar));
|
||||
|
||||
if (!UnsafeStackPtr) {
|
||||
// The global variable is not defined yet, define it ourselves.
|
||||
// We use the initial-exec TLS model because we do not support the
|
||||
// variable living anywhere other than in the main executable.
|
||||
UnsafeStackPtr = new GlobalVariable(
|
||||
/*Module=*/M, /*Type=*/StackPtrTy,
|
||||
/*isConstant=*/false, /*Linkage=*/GlobalValue::ExternalLinkage,
|
||||
/*Initializer=*/0, /*Name=*/kUnsafeStackPtrVar,
|
||||
/*InsertBefore=*/nullptr,
|
||||
/*ThreadLocalMode=*/GlobalValue::InitialExecTLSModel);
|
||||
} else {
|
||||
// The variable exists, check its type and attributes.
|
||||
if (UnsafeStackPtr->getValueType() != StackPtrTy) {
|
||||
report_fatal_error(Twine(kUnsafeStackPtrVar) + " must have void* type");
|
||||
}
|
||||
|
||||
if (!UnsafeStackPtr->isThreadLocal()) {
|
||||
report_fatal_error(Twine(kUnsafeStackPtrVar) + " must be thread-local");
|
||||
}
|
||||
}
|
||||
return UnsafeStackPtr;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeStack::findInsts(Function &F,
|
||||
@ -349,13 +376,12 @@ SafeStack::createStackRestorePoints(Function &F,
|
||||
}
|
||||
|
||||
Value *
|
||||
SafeStack::moveStaticAllocasToUnsafeStack(Function &F,
|
||||
SafeStack::moveStaticAllocasToUnsafeStack(IRBuilder<> &IRB, Function &F,
|
||||
ArrayRef<AllocaInst *> StaticAllocas,
|
||||
ArrayRef<ReturnInst *> Returns) {
|
||||
if (StaticAllocas.empty())
|
||||
return nullptr;
|
||||
|
||||
IRBuilder<> IRB(F.getEntryBlock().getFirstInsertionPt());
|
||||
DIBuilder DIB(*F.getParent());
|
||||
|
||||
// We explicitly compute and set the unsafe stack layout for all unsafe
|
||||
@ -511,8 +537,6 @@ void SafeStack::moveDynamicAllocasToUnsafeStack(
|
||||
}
|
||||
|
||||
bool SafeStack::runOnFunction(Function &F) {
|
||||
auto AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
|
||||
|
||||
DEBUG(dbgs() << "[SafeStack] Function: " << F.getName() << "\n");
|
||||
|
||||
if (!F.hasFnAttribute(Attribute::SafeStack)) {
|
||||
@ -527,6 +551,10 @@ bool SafeStack::runOnFunction(Function &F) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
|
||||
|
||||
TLI = TM ? TM->getSubtargetImpl(F)->getTargetLowering() : nullptr;
|
||||
|
||||
{
|
||||
// Make sure the regular stack protector won't run on this function
|
||||
// (safestack attribute takes precedence).
|
||||
@ -572,11 +600,11 @@ bool SafeStack::runOnFunction(Function &F) {
|
||||
if (!StackRestorePoints.empty())
|
||||
++NumUnsafeStackRestorePointsFunctions;
|
||||
|
||||
if (!UnsafeStackPtr)
|
||||
UnsafeStackPtr = getOrCreateUnsafeStackPtr(*F.getParent());
|
||||
IRBuilder<> IRB(F.begin()->getFirstInsertionPt());
|
||||
UnsafeStackPtr = getOrCreateUnsafeStackPtr(IRB, F);
|
||||
|
||||
// The top of the unsafe stack after all unsafe static allocas are allocated.
|
||||
Value *StaticTop = moveStaticAllocasToUnsafeStack(F, StaticAllocas, Returns);
|
||||
Value *StaticTop = moveStaticAllocasToUnsafeStack(IRB, F, StaticAllocas, Returns);
|
||||
|
||||
// Safe stack object that stores the current unsafe stack top. It is updated
|
||||
// as unsafe dynamic (non-constant-sized) allocas are allocated and freed.
|
||||
@ -598,9 +626,11 @@ bool SafeStack::runOnFunction(Function &F) {
|
||||
} // end anonymous namespace
|
||||
|
||||
char SafeStack::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(SafeStack, "safe-stack",
|
||||
"Safe Stack instrumentation pass", false, false)
|
||||
INITIALIZE_PASS_END(SafeStack, "safe-stack", "Safe Stack instrumentation pass",
|
||||
false, false)
|
||||
INITIALIZE_TM_PASS_BEGIN(SafeStack, "safe-stack",
|
||||
"Safe Stack instrumentation pass", false, false)
|
||||
INITIALIZE_TM_PASS_END(SafeStack, "safe-stack",
|
||||
"Safe Stack instrumentation pass", false, false)
|
||||
|
||||
FunctionPass *llvm::createSafeStackPass() { return new SafeStack(); }
|
||||
FunctionPass *llvm::createSafeStackPass(const llvm::TargetMachine *TM) {
|
||||
return new SafeStack(TM);
|
||||
}
|
||||
|
18
test/Transforms/SafeStack/AArch64/abi.ll
Normal file
18
test/Transforms/SafeStack/AArch64/abi.ll
Normal file
@ -0,0 +1,18 @@
|
||||
; RUN: opt -safe-stack -S -mtriple=aarch64-linux-android < %s -o - | FileCheck %s
|
||||
|
||||
|
||||
define void @foo() nounwind uwtable safestack {
|
||||
entry:
|
||||
; CHECK: %[[SPA:.*]] = call i8** @__safestack_pointer_address()
|
||||
; CHECK: %[[USP:.*]] = load i8*, i8** %[[SPA]]
|
||||
; CHECK: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
|
||||
; CHECK: store i8* %[[USST]], i8** %[[SPA]]
|
||||
|
||||
%a = alloca i8, align 8
|
||||
call void @Capture(i8* %a)
|
||||
|
||||
; CHECK: store i8* %[[USP]], i8** %[[SPA]]
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @Capture(i8*)
|
3
test/Transforms/SafeStack/AArch64/lit.local.cfg
Normal file
3
test/Transforms/SafeStack/AArch64/lit.local.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
if not 'AArch64' in config.root.targets:
|
||||
config.unsupported = True
|
||||
|
18
test/Transforms/SafeStack/ARM/abi.ll
Normal file
18
test/Transforms/SafeStack/ARM/abi.ll
Normal file
@ -0,0 +1,18 @@
|
||||
; RUN: opt -safe-stack -S -mtriple=arm-linux-android < %s -o - | FileCheck %s
|
||||
|
||||
|
||||
define void @foo() nounwind uwtable safestack {
|
||||
entry:
|
||||
; CHECK: %[[SPA:.*]] = call i8** @__safestack_pointer_address()
|
||||
; CHECK: %[[USP:.*]] = load i8*, i8** %[[SPA]]
|
||||
; CHECK: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
|
||||
; CHECK: store i8* %[[USST]], i8** %[[SPA]]
|
||||
|
||||
%a = alloca i8, align 8
|
||||
call void @Capture(i8* %a)
|
||||
|
||||
; CHECK: store i8* %[[USP]], i8** %[[SPA]]
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @Capture(i8*)
|
3
test/Transforms/SafeStack/ARM/lit.local.cfg
Normal file
3
test/Transforms/SafeStack/ARM/lit.local.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
if not 'ARM' in config.root.targets:
|
||||
config.unsupported = True
|
||||
|
30
test/Transforms/SafeStack/X86/abi.ll
Normal file
30
test/Transforms/SafeStack/X86/abi.ll
Normal file
@ -0,0 +1,30 @@
|
||||
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s --check-prefix=TLS
|
||||
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s --check-prefix=TLS
|
||||
; RUN: opt -safe-stack -S -mtriple=i686-linux-android < %s -o - | FileCheck %s --check-prefix=DIRECT-TLS32
|
||||
; RUN: opt -safe-stack -S -mtriple=x86_64-linux-android < %s -o - | FileCheck %s --check-prefix=DIRECT-TLS64
|
||||
|
||||
|
||||
define void @foo() nounwind uwtable safestack {
|
||||
entry:
|
||||
; TLS: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
|
||||
; TLS: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
|
||||
; TLS: store i8* %[[USST]], i8** @__safestack_unsafe_stack_ptr
|
||||
|
||||
; DIRECT-TLS32: %[[USP:.*]] = load i8*, i8* addrspace(36)* inttoptr (i32 256 to i8* addrspace(36)*)
|
||||
; DIRECT-TLS32: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
|
||||
; DIRECT-TLS32: store i8* %[[USST]], i8* addrspace(36)* inttoptr (i32 256 to i8* addrspace(36)*)
|
||||
|
||||
; DIRECT-TLS64: %[[USP:.*]] = load i8*, i8* addrspace(72)* inttoptr (i32 257 to i8* addrspace(72)*)
|
||||
; DIRECT-TLS64: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
|
||||
; DIRECT-TLS64: store i8* %[[USST]], i8* addrspace(72)* inttoptr (i32 257 to i8* addrspace(72)*)
|
||||
|
||||
%a = alloca i8, align 8
|
||||
call void @Capture(i8* %a)
|
||||
|
||||
; TLS: store i8* %[[USP]], i8** @__safestack_unsafe_stack_ptr
|
||||
; DIRECT-TLS32: store i8* %[[USP]], i8* addrspace(36)* inttoptr (i32 256 to i8* addrspace(36)*)
|
||||
; DIRECT-TLS64: store i8* %[[USP]], i8* addrspace(72)* inttoptr (i32 257 to i8* addrspace(72)*)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @Capture(i8*)
|
3
test/Transforms/SafeStack/X86/lit.local.cfg
Normal file
3
test/Transforms/SafeStack/X86/lit.local.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
if not 'X86' in config.root.targets:
|
||||
config.unsupported = True
|
||||
|
Loading…
Reference in New Issue
Block a user