From c7b6dc0535d6f0cffeddced49bb6b2d11f721901 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Wed, 23 Sep 2015 01:03:51 +0000 Subject: [PATCH] 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). git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@248357 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Target/TargetLowering.h | 8 ++ include/llvm/Transforms/Instrumentation.h | 4 +- lib/CodeGen/Passes.cpp | 2 +- lib/Target/X86/X86ISelLowering.cpp | 23 ++++ lib/Target/X86/X86ISelLowering.h | 6 + lib/Target/X86/X86Subtarget.h | 3 + lib/Transforms/Instrumentation/SafeStack.cpp | 111 ++++++++++++------- test/Transforms/SafeStack/abi.ll | 38 +++++++ 8 files changed, 153 insertions(+), 42 deletions(-) create mode 100644 test/Transforms/SafeStack/abi.ll diff --git a/include/llvm/Target/TargetLowering.h b/include/llvm/Target/TargetLowering.h index caf28d2b118..64e0ee55030 100644 --- a/include/llvm/Target/TargetLowering.h +++ b/include/llvm/Target/TargetLowering.h @@ -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; diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h index da4c0782cb9..cfe6b432895 100644 --- a/include/llvm/Transforms/Instrumentation.h +++ b/include/llvm/Transforms/Instrumentation.h @@ -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 diff --git a/lib/CodeGen/Passes.cpp b/lib/CodeGen/Passes.cpp index 7ea7b6e45ce..114c9d11be8 100644 --- a/lib/CodeGen/Passes.cpp +++ b/lib/CodeGen/Passes.cpp @@ -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) diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index fbead736ec0..d366e280739 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -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!"); diff --git a/lib/Target/X86/X86ISelLowering.h b/lib/Target/X86/X86ISelLowering.h index 949c6511bde..ff680a52b1a 100644 --- a/lib/Target/X86/X86ISelLowering.h +++ b/lib/Target/X86/X86ISelLowering.h @@ -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; diff --git a/lib/Target/X86/X86Subtarget.h b/lib/Target/X86/X86Subtarget.h index c5d74e66b7b..52e68c08180 100644 --- a/lib/Target/X86/X86Subtarget.h +++ b/lib/Target/X86/X86Subtarget.h @@ -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(); } diff --git a/lib/Transforms/Instrumentation/SafeStack.cpp b/lib/Transforms/Instrumentation/SafeStack.cpp index fdf96cc130a..6e5e85f0825 100644 --- a/lib/Transforms/Instrumentation/SafeStack.cpp +++ b/lib/Transforms/Instrumentation/SafeStack.cpp @@ -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 StaticAllocas, ArrayRef 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(); @@ -237,35 +247,53 @@ 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(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->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(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 +377,12 @@ SafeStack::createStackRestorePoints(Function &F, } Value * -SafeStack::moveStaticAllocasToUnsafeStack(Function &F, +SafeStack::moveStaticAllocasToUnsafeStack(IRBuilder<> &IRB, Function &F, ArrayRef StaticAllocas, ArrayRef 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 @@ -513,6 +540,8 @@ void SafeStack::moveDynamicAllocasToUnsafeStack( bool SafeStack::runOnFunction(Function &F) { auto AA = &getAnalysis().getAAResults(); + TLI = TM->getSubtargetImpl(F)->getTargetLowering(); + DEBUG(dbgs() << "[SafeStack] Function: " << F.getName() << "\n"); if (!F.hasFnAttribute(Attribute::SafeStack)) { @@ -572,11 +601,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 +627,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); +} diff --git a/test/Transforms/SafeStack/abi.ll b/test/Transforms/SafeStack/abi.ll new file mode 100644 index 00000000000..c8d0553e527 --- /dev/null +++ b/test/Transforms/SafeStack/abi.ll @@ -0,0 +1,38 @@ +; 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 +; RUN: opt -safe-stack -S -mtriple=arm-linux-android < %s -o - | FileCheck %s --check-prefix=CALL +; RUN: opt -safe-stack -S -mtriple=aarch64-linux-android < %s -o - | FileCheck %s --check-prefix=CALL + + +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)*) + +; CALL: %[[SPA:.*]] = call i8** @__safestack_pointer_address() +; CALL: %[[USP:.*]] = load i8*, i8** %[[SPA]] +; CALL: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16 +; CALL: store i8* %[[USST]], i8** %[[SPA]] + + %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)*) +; CALL: store i8* %[[USP]], i8** %[[SPA]] + ret void +} + +declare void @Capture(i8*)