|
From bd09fa121deae36efa10d52b677faab55d86600b Mon Sep 17 00:00:00 2001 |
|
From: Daniel Micay <danielmicay@gmail.com> |
|
Date: Sat, 2 Jul 2016 18:19:07 -0400 |
|
Subject: [PATCH] enhanced stack protector |
|
|
|
This is an extension of the stack protector aimed at protecting the |
|
return address against more than simple linear overflows. Immunix |
|
StackGuard 1.21 from 1999 was an early implementation of this. |
|
|
|
Before storing the canary value to the stack in the function prologue, |
|
the return address of the function is mixed in with XOR. The epilogue |
|
validates the canary by attempting to obtain the original canary value |
|
via another XOR of the return address before checking it. This results |
|
in the return address being validated directly. |
|
|
|
This is primarily intended to be used with -fstack-protector-all, since |
|
otherwise there will be unprotected return addresses. |
|
|
|
A new internal returnaddress intrinsic is used in order to prevent the |
|
load in the prologue and epilogue from being combined. |
|
--- |
|
include/llvm/CodeGen/ISDOpcodes.h | 4 +++ |
|
include/llvm/IR/Intrinsics.td | 1 + |
|
lib/CodeGen/IntrinsicLowering.cpp | 7 +++++- |
|
lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 2 ++ |
|
lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 18 +++++++++++--- |
|
lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp | 1 + |
|
lib/CodeGen/StackProtector.cpp | 31 +++++++++++++++++++----- |
|
lib/Target/AArch64/AArch64ISelLowering.cpp | 25 +++++++++++++++++++ |
|
lib/Target/AArch64/AArch64ISelLowering.h | 1 + |
|
lib/Target/X86/X86ISelLowering.cpp | 19 +++++++++++++++ |
|
lib/Target/X86/X86ISelLowering.h | 1 + |
|
11 files changed, 100 insertions(+), 10 deletions(-) |
|
|
|
diff --git a/include/llvm/CodeGen/ISDOpcodes.h b/include/llvm/CodeGen/ISDOpcodes.h |
|
index 89cb7a8..89b43c4 100644 |
|
--- a/include/llvm/CodeGen/ISDOpcodes.h |
|
+++ b/include/llvm/CodeGen/ISDOpcodes.h |
|
@@ -72,6 +72,10 @@ namespace ISD { |
|
/// the parent's frame or return address, and so on. |
|
FRAMEADDR, RETURNADDR, |
|
|
|
+ // SSP_RETURNADDR - Represents the llvm.stackprotector_returnaddress |
|
+ // intrinsic. |
|
+ SSP_RETURNADDR, |
|
+ |
|
/// LOCAL_RECOVER - Represents the llvm.localrecover intrinsic. |
|
/// Materializes the offset from the local object pointer of another |
|
/// function to a particular local object passed to llvm.localescape. The |
|
diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td |
|
index 5ece731..c7cfb3b 100644 |
|
--- a/include/llvm/IR/Intrinsics.td |
|
+++ b/include/llvm/IR/Intrinsics.td |
|
@@ -339,6 +339,7 @@ def int_assume : Intrinsic<[], [llvm_i1_ty], []>; |
|
// guard to the correct place on the stack frame. |
|
def int_stackprotector : Intrinsic<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>; |
|
def int_stackguard : Intrinsic<[llvm_ptr_ty], [], []>; |
|
+def int_stackprotector_returnaddress : Intrinsic<[llvm_ptr_ty], [], []>; |
|
|
|
// A counter increment for instrumentation based profiling. |
|
def int_instrprof_increment : Intrinsic<[], |
|
diff --git a/lib/CodeGen/IntrinsicLowering.cpp b/lib/CodeGen/IntrinsicLowering.cpp |
|
index 2962f87..be48bdb 100644 |
|
--- a/lib/CodeGen/IntrinsicLowering.cpp |
|
+++ b/lib/CodeGen/IntrinsicLowering.cpp |
|
@@ -439,7 +439,12 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) { |
|
CI->replaceAllUsesWith(ConstantPointerNull::get( |
|
cast<PointerType>(CI->getType()))); |
|
break; |
|
- |
|
+ case Intrinsic::stackprotector_returnaddress: |
|
+ errs() << "WARNING: this target does not support the" |
|
+ " llvm.stackprotector_returnaddress intrinsic.\n"; |
|
+ CI->replaceAllUsesWith(ConstantPointerNull::get( |
|
+ cast<PointerType>(CI->getType()))); |
|
+ break; |
|
case Intrinsic::prefetch: |
|
break; // Simply strip out prefetches on unsupported architectures |
|
|
|
diff --git a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp |
|
index e81ea0d..b664be3 100644 |
|
--- a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp |
|
+++ b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp |
|
@@ -1030,6 +1030,7 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { |
|
case ISD::ADJUST_TRAMPOLINE: |
|
case ISD::FRAMEADDR: |
|
case ISD::RETURNADDR: |
|
+ case ISD::SSP_RETURNADDR: |
|
// These operations lie about being legal: when they claim to be legal, |
|
// they should actually be custom-lowered. |
|
Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0)); |
|
@@ -2760,6 +2761,7 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) { |
|
break; |
|
case ISD::FRAMEADDR: |
|
case ISD::RETURNADDR: |
|
+ case ISD::SSP_RETURNADDR: |
|
case ISD::FRAME_TO_ARGS_OFFSET: |
|
Results.push_back(DAG.getConstant(0, dl, Node->getValueType(0))); |
|
break; |
|
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp |
|
index 27ee96d..6b3eb6c 100644 |
|
--- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp |
|
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp |
|
@@ -2080,7 +2080,8 @@ void SelectionDAGBuilder::visitSPDescriptorParent(StackProtectorDescriptor &SPD, |
|
// If useLoadStackGuardNode returns true, generate LOAD_STACK_GUARD. |
|
// Otherwise, emit a volatile load to retrieve the stack guard value. |
|
SDValue Chain = DAG.getEntryNode(); |
|
- if (TLI.useLoadStackGuardNode()) { |
|
+ bool XorCanary = true; |
|
+ if (TLI.useLoadStackGuardNode() && !XorCanary) { |
|
Guard = getLoadStackGuard(DAG, dl, Chain); |
|
} else { |
|
const Value *IRGuard = TLI.getSDagStackGuard(M); |
|
@@ -5354,7 +5355,8 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { |
|
MachineFunction &MF = DAG.getMachineFunction(); |
|
const Module &M = *MF.getFunction()->getParent(); |
|
SDValue Chain = getRoot(); |
|
- if (TLI.useLoadStackGuardNode()) { |
|
+ bool XorCanary = true; |
|
+ if (TLI.useLoadStackGuardNode() && !XorCanary) { |
|
Res = getLoadStackGuard(DAG, sdl, Chain); |
|
} else { |
|
const Value *Global = TLI.getSDagStackGuard(M); |
|
@@ -5374,7 +5376,8 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { |
|
EVT PtrTy = TLI.getPointerTy(DAG.getDataLayout()); |
|
SDValue Src, Chain = getRoot(); |
|
|
|
- if (TLI.useLoadStackGuardNode()) |
|
+ bool XorCanary = true; |
|
+ if (TLI.useLoadStackGuardNode() && !XorCanary) |
|
Src = getLoadStackGuard(DAG, sdl, Chain); |
|
else |
|
Src = getValue(I.getArgOperand(0)); // The guard's value. |
|
@@ -5394,6 +5397,15 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { |
|
DAG.setRoot(Res); |
|
return nullptr; |
|
} |
|
+ case Intrinsic::stackprotector_returnaddress: { |
|
+ SDValue Chain = getRoot(); |
|
+ Res = DAG.getNode(ISD::SSP_RETURNADDR, sdl, |
|
+ DAG.getVTList(TLI.getPointerTy(DAG.getDataLayout()), MVT::Other), |
|
+ Chain); |
|
+ setValue(&I, Res); |
|
+ DAG.setRoot(Res.getValue(1)); |
|
+ return nullptr; |
|
+ } |
|
case Intrinsic::objectsize: { |
|
// If we don't know by now, we're never going to know. |
|
ConstantInt *CI = dyn_cast<ConstantInt>(I.getArgOperand(1)); |
|
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp |
|
index 401da05..bfdf694 100644 |
|
--- a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp |
|
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp |
|
@@ -100,6 +100,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { |
|
case ISD::JumpTable: return "JumpTable"; |
|
case ISD::GLOBAL_OFFSET_TABLE: return "GLOBAL_OFFSET_TABLE"; |
|
case ISD::RETURNADDR: return "RETURNADDR"; |
|
+ case ISD::SSP_RETURNADDR: return "SSP_RETURNADDR"; |
|
case ISD::FRAMEADDR: return "FRAMEADDR"; |
|
case ISD::LOCAL_RECOVER: return "LOCAL_RECOVER"; |
|
case ISD::READ_REGISTER: return "READ_REGISTER"; |
|
diff --git a/lib/CodeGen/StackProtector.cpp b/lib/CodeGen/StackProtector.cpp |
|
index 89868e4..e39ca9a 100644 |
|
--- a/lib/CodeGen/StackProtector.cpp |
|
+++ b/lib/CodeGen/StackProtector.cpp |
|
@@ -305,6 +305,18 @@ static Value *getStackGuard(const TargetLoweringBase *TLI, Module *M, |
|
return B.CreateCall(Intrinsic::getDeclaration(M, Intrinsic::stackguard)); |
|
} |
|
|
|
+static Value *XorReturnAddress(Module *M, IRBuilder<> &B, Value *Ptr) { |
|
+ const DataLayout &DL = M->getDataLayout(); |
|
+ Type *IntTy = DL.getIntPtrType(Ptr->getType()); |
|
+ |
|
+ Value *RetPtr = B.CreateCall( |
|
+ Intrinsic::getDeclaration(M, Intrinsic::stackprotector_returnaddress)); |
|
+ |
|
+ Value *IntPtr = B.CreatePtrToInt(Ptr, IntTy); |
|
+ Value *IntRetPtr = B.CreatePtrToInt(RetPtr, IntTy); |
|
+ return B.CreateIntToPtr(B.CreateXor(IntPtr, IntRetPtr), Ptr->getType()); |
|
+} |
|
+ |
|
/// Insert code into the entry block that stores the stack guard |
|
/// variable onto the stack: |
|
/// |
|
@@ -323,8 +335,10 @@ static bool CreatePrologue(Function *F, Module *M, ReturnInst *RI, |
|
AI = B.CreateAlloca(PtrTy, nullptr, "StackGuardSlot"); |
|
|
|
Value *GuardSlot = getStackGuard(TLI, M, B, &SupportsSelectionDAGSP); |
|
+ Value *XorGuard = XorReturnAddress(M, B, GuardSlot); |
|
+ |
|
B.CreateCall(Intrinsic::getDeclaration(M, Intrinsic::stackprotector), |
|
- {GuardSlot, AI}); |
|
+ {XorGuard, AI}); |
|
return SupportsSelectionDAGSP; |
|
} |
|
|
|
@@ -335,8 +349,9 @@ static bool CreatePrologue(Function *F, Module *M, ReturnInst *RI, |
|
/// - The epilogue checks the value stored in the prologue against the original |
|
/// value. It calls __stack_chk_fail if they differ. |
|
bool StackProtector::InsertStackProtectors() { |
|
+ bool XorCanary = true; |
|
bool SupportsSelectionDAGSP = |
|
- EnableSelectionDAGSP && !TM->Options.EnableFastISel; |
|
+ EnableSelectionDAGSP && !TM->Options.EnableFastISel && !XorCanary; |
|
AllocaInst *AI = nullptr; // Place on stack that stores the stack guard. |
|
|
|
for (Function::iterator I = F->begin(), E = F->end(); I != E;) { |
|
@@ -368,8 +383,10 @@ bool StackProtector::InsertStackProtectors() { |
|
// Generate the function-based epilogue instrumentation. |
|
// The target provides a guard check function, generate a call to it. |
|
IRBuilder<> B(RI); |
|
- LoadInst *Guard = B.CreateLoad(AI, true, "Guard"); |
|
- CallInst *Call = B.CreateCall(GuardCheck, {Guard}); |
|
+ Value *Guard = B.CreateLoad(AI, true, "Guard"); |
|
+ Value *XorGuard = XorReturnAddress(M, B, Guard); |
|
+ |
|
+ CallInst *Call = B.CreateCall(GuardCheck, {XorGuard}); |
|
llvm::Function *Function = cast<llvm::Function>(GuardCheck); |
|
Call->setAttributes(Function->getAttributes()); |
|
Call->setCallingConv(Function->getCallingConv()); |
|
@@ -424,8 +441,10 @@ bool StackProtector::InsertStackProtectors() { |
|
// Generate the stack protector instructions in the old basic block. |
|
IRBuilder<> B(BB); |
|
Value *Guard = getStackGuard(TLI, M, B); |
|
- LoadInst *LI2 = B.CreateLoad(AI, true); |
|
- Value *Cmp = B.CreateICmpEQ(Guard, LI2); |
|
+ Value *LI2 = B.CreateLoad(AI, true); |
|
+ Value *XorGuard = XorReturnAddress(M, B, LI2); |
|
+ |
|
+ Value *Cmp = B.CreateICmpEQ(Guard, XorGuard); |
|
auto SuccessProb = |
|
BranchProbabilityInfo::getBranchProbStackProtector(true); |
|
auto FailureProb = |
|
diff --git a/lib/Target/AArch64/AArch64ISelLowering.cpp b/lib/Target/AArch64/AArch64ISelLowering.cpp |
|
index d7d4032..e6119c0 100644 |
|
--- a/lib/Target/AArch64/AArch64ISelLowering.cpp |
|
+++ b/lib/Target/AArch64/AArch64ISelLowering.cpp |
|
@@ -2362,6 +2362,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, |
|
return LowerFRAMEADDR(Op, DAG); |
|
case ISD::RETURNADDR: |
|
return LowerRETURNADDR(Op, DAG); |
|
+ case ISD::SSP_RETURNADDR: |
|
+ return LowerSSP_RETURNADDR(Op, DAG); |
|
case ISD::INSERT_VECTOR_ELT: |
|
return LowerINSERT_VECTOR_ELT(Op, DAG); |
|
case ISD::EXTRACT_VECTOR_ELT: |
|
@@ -4424,6 +4426,29 @@ SDValue AArch64TargetLowering::LowerRETURNADDR(SDValue Op, |
|
return DAG.getCopyFromReg(DAG.getEntryNode(), DL, Reg, VT); |
|
} |
|
|
|
+SDValue AArch64TargetLowering::LowerSSP_RETURNADDR(SDValue Op, |
|
+ SelectionDAG &DAG) const { |
|
+ SDValue Chain = Op.getOperand(0); |
|
+ SmallVector<SDValue, 2> Results; |
|
+ MachineFunction &MF = DAG.getMachineFunction(); |
|
+ MachineFrameInfo *MFI = MF.getFrameInfo(); |
|
+ MFI->setReturnAddressIsTaken(true); |
|
+ MFI->setFrameAddressIsTaken(true); |
|
+ |
|
+ EVT VT = Op.getValueType(); |
|
+ SDLoc DL(Op); |
|
+ |
|
+ SDValue FrameAddr = |
|
+ DAG.getCopyFromReg(DAG.getEntryNode(), DL, AArch64::FP, VT); |
|
+ SDValue Offset = DAG.getConstant(8, DL, getPointerTy(DAG.getDataLayout())); |
|
+ |
|
+ Results.push_back(DAG.getLoad(VT, DL, Chain, |
|
+ DAG.getNode(ISD::ADD, DL, VT, FrameAddr, Offset), |
|
+ MachinePointerInfo(), true, false, false, 0)); |
|
+ Results.push_back(Chain); |
|
+ return DAG.getMergeValues(Results, DL); |
|
+} |
|
+ |
|
/// LowerShiftRightParts - Lower SRA_PARTS, which returns two |
|
/// i64 values and take a 2 x i64 value to shift plus a shift amount. |
|
SDValue AArch64TargetLowering::LowerShiftRightParts(SDValue Op, |
|
diff --git a/lib/Target/AArch64/AArch64ISelLowering.h b/lib/Target/AArch64/AArch64ISelLowering.h |
|
index c87cfed..1c5177a 100644 |
|
--- a/lib/Target/AArch64/AArch64ISelLowering.h |
|
+++ b/lib/Target/AArch64/AArch64ISelLowering.h |
|
@@ -495,6 +495,7 @@ private: |
|
SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; |
|
+ SDValue LowerSSP_RETURNADDR(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerEXTRACT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerSCALAR_TO_VECTOR(SDValue Op, SelectionDAG &DAG) const; |
|
diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp |
|
index 975e61d..30ef45c 100644 |
|
--- a/lib/Target/X86/X86ISelLowering.cpp |
|
+++ b/lib/Target/X86/X86ISelLowering.cpp |
|
@@ -18582,6 +18582,24 @@ SDValue X86TargetLowering::LowerRETURNADDR(SDValue Op, |
|
RetAddrFI, MachinePointerInfo(), false, false, false, 0); |
|
} |
|
|
|
+SDValue X86TargetLowering::LowerSSP_RETURNADDR(SDValue Op, |
|
+ SelectionDAG &DAG) const { |
|
+ SDValue Chain = Op.getOperand(0); |
|
+ SmallVector<SDValue, 2> Results; |
|
+ MachineFrameInfo *MFI = DAG.getMachineFunction().getFrameInfo(); |
|
+ MFI->setReturnAddressIsTaken(true); |
|
+ |
|
+ SDLoc dl(Op); |
|
+ EVT PtrVT = getPointerTy(DAG.getDataLayout()); |
|
+ |
|
+ // Just load the return address. |
|
+ SDValue RetAddrFI = getReturnAddressFrameIndex(DAG); |
|
+ Results.push_back(DAG.getLoad(PtrVT, dl, Chain, |
|
+ RetAddrFI, MachinePointerInfo(), true, false, false, 0)); |
|
+ Results.push_back(Chain); |
|
+ return DAG.getMergeValues(Results, dl); |
|
+} |
|
+ |
|
SDValue X86TargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const { |
|
MachineFunction &MF = DAG.getMachineFunction(); |
|
MachineFrameInfo *MFI = MF.getFrameInfo(); |
|
@@ -21715,6 +21733,7 @@ SDValue X86TargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { |
|
case ISD::INTRINSIC_VOID: |
|
case ISD::INTRINSIC_W_CHAIN: return LowerINTRINSIC_W_CHAIN(Op, Subtarget, DAG); |
|
case ISD::RETURNADDR: return LowerRETURNADDR(Op, DAG); |
|
+ case ISD::SSP_RETURNADDR: return LowerSSP_RETURNADDR(Op, DAG); |
|
case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG); |
|
case ISD::FRAME_TO_ARGS_OFFSET: |
|
return LowerFRAME_TO_ARGS_OFFSET(Op, DAG); |
|
diff --git a/lib/Target/X86/X86ISelLowering.h b/lib/Target/X86/X86ISelLowering.h |
|
index d826f1e..829cff7 100644 |
|
--- a/lib/Target/X86/X86ISelLowering.h |
|
+++ b/lib/Target/X86/X86ISelLowering.h |
|
@@ -1101,6 +1101,7 @@ namespace llvm { |
|
SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; |
|
+ SDValue LowerSSP_RETURNADDR(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerFRAME_TO_ARGS_OFFSET(SDValue Op, SelectionDAG &DAG) const; |
|
SDValue LowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const; |
|
-- |
|
2.9.0 |