[stack-protection] Add support for MSVC buffer security check
Summary: This patch is adding support for the MSVC buffer security check implementation The buffer security check is turned on with the '/GS' compiler switch. * https://msdn.microsoft.com/en-us/library/8dbf701c.aspx * To be added to clang here: http://reviews.llvm.org/D20347 Some overview of buffer security check feature and implementation: * https://msdn.microsoft.com/en-us/library/aa290051(VS.71).aspx * http://www.ksyash.com/2011/01/buffer-overflow-protection-3/ * http://blog.osom.info/2012/02/understanding-vs-c-compilers-buffer.html For the following example: ``` int example(int offset, int index) { char buffer[10]; memset(buffer, 0xCC, index); return buffer[index]; } ``` The MSVC compiler is adding these instructions to perform stack integrity check: ``` push ebp mov ebp,esp sub esp,50h [1] mov eax,dword ptr [__security_cookie (01068024h)] [2] xor eax,ebp [3] mov dword ptr [ebp-4],eax push ebx push esi push edi mov eax,dword ptr [index] push eax push 0CCh lea ecx,[buffer] push ecx call _memset (010610B9h) add esp,0Ch mov eax,dword ptr [index] movsx eax,byte ptr buffer[eax] pop edi pop esi pop ebx [4] mov ecx,dword ptr [ebp-4] [5] xor ecx,ebp [6] call @__security_check_cookie@4 (01061276h) mov esp,ebp pop ebp ret ``` The instrumentation above is: * [1] is loading the global security canary, * [3] is storing the local computed ([2]) canary to the guard slot, * [4] is loading the guard slot and ([5]) re-compute the global canary, * [6] is validating the resulting canary with the '__security_check_cookie' and performs error handling. Overview of the current stack-protection implementation: * lib/CodeGen/StackProtector.cpp * There is a default stack-protection implementation applied on intermediate representation. * The target can overload 'getIRStackGuard' method if it has a standard location for the stack protector cookie. * An intrinsic 'Intrinsic::stackprotector' is added to the prologue. It will be expanded by the instruction selection pass (DAG or Fast). * Basic Blocks are added to every instrumented function to receive the code for handling stack guard validation and errors handling. * Guard manipulation and comparison are added directly to the intermediate representation. * lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp * lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp * There is an implementation that adds instrumentation during instruction selection (for better handling of sibbling calls). * see long comment above 'class StackProtectorDescriptor' declaration. * The target needs to override 'getSDagStackGuard' to activate SDAG stack protection generation. (note: getIRStackGuard MUST be nullptr). * 'getSDagStackGuard' returns the appropriate stack guard (security cookie) * The code is generated by 'SelectionDAGBuilder.cpp' and 'SelectionDAGISel.cpp'. * include/llvm/Target/TargetLowering.h * Contains function to retrieve the default Guard 'Value'; should be overriden by each target to select which implementation is used and provide Guard 'Value'. * lib/Target/X86/X86ISelLowering.cpp * Contains the x86 specialisation; Guard 'Value' used by the SelectionDAG algorithm. Function-based Instrumentation: * The MSVC doesn't inline the stack guard comparison in every function. Instead, a call to '__security_check_cookie' is added to the epilogue before every return instructions. * To support function-based instrumentation, this patch is * adding a function to get the function-based check (llvm 'Value', see include/llvm/Target/TargetLowering.h), * If provided, the stack protection instrumentation won't be inlined and a call to that function will be added to the prologue. * modifying (SelectionDAGISel.cpp) do avoid producing basic blocks used for inline instrumentation, * generating the function-based instrumentation during the ISEL pass (SelectionDAGBuilder.cpp), * if FastISEL (not SelectionDAG), using the fallback which rely on the same function-based implemented over intermediate representation (StackProtector.cpp). Modifications * adding support for MSVC (lib/Target/X86/X86ISelLowering.cpp) * adding support function-based instrumentation (lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp, .h) Results * IR generated instrumentation: ``` clang-cl /GS test.cc /Od /c -mllvm -print-isel-input ``` ``` *** Final LLVM Code input to ISel *** ; Function Attrs: nounwind sspstrong define i32 @"\01?example@@YAHHH@Z"(i32 %offset, i32 %index) #0 { entry: %StackGuardSlot = alloca i8* <<<-- Allocated guard slot %0 = call i8* @llvm.stackguard() <<<-- Loading Stack Guard value call void @llvm.stackprotector(i8* %0, i8** %StackGuardSlot) <<<-- Prologue intrinsic call (store to Guard slot) %index.addr = alloca i32, align 4 %offset.addr = alloca i32, align 4 %buffer = alloca [10 x i8], align 1 store i32 %index, i32* %index.addr, align 4 store i32 %offset, i32* %offset.addr, align 4 %arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %buffer, i32 0, i32 0 %1 = load i32, i32* %index.addr, align 4 call void @llvm.memset.p0i8.i32(i8* %arraydecay, i8 -52, i32 %1, i32 1, i1 false) %2 = load i32, i32* %index.addr, align 4 %arrayidx = getelementptr inbounds [10 x i8], [10 x i8]* %buffer, i32 0, i32 %2 %3 = load i8, i8* %arrayidx, align 1 %conv = sext i8 %3 to i32 %4 = load volatile i8*, i8** %StackGuardSlot <<<-- Loading Guard slot call void @__security_check_cookie(i8* %4) <<<-- Epilogue function-based check ret i32 %conv } ``` * SelectionDAG generated instrumentation: ``` clang-cl /GS test.cc /O1 /c /FA ``` ``` "?example@@YAHHH@Z": # @"\01?example@@YAHHH@Z" # BB#0: # %entry pushl %esi subl $16, %esp movl ___security_cookie, %eax <<<-- Loading Stack Guard value movl 28(%esp), %esi movl %eax, 12(%esp) <<<-- Store to Guard slot leal 2(%esp), %eax pushl %esi pushl $204 pushl %eax calll _memset addl $12, %esp movsbl 2(%esp,%esi), %esi movl 12(%esp), %ecx <<<-- Loading Guard slot calll @__security_check_cookie@4 <<<-- Epilogue function-based check movl %esi, %eax addl $16, %esp popl %esi retl ``` Reviewers: kcc, pcc, eugenis, rnk Subscribers: majnemer, llvm-commits, hans, thakis, rnk Differential Revision: http://reviews.llvm.org/D20346 llvm-svn: 272053
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/BranchProbabilityInfo.h"
|
||||
#include "llvm/Analysis/EHPersonalities.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/CodeGen/Analysis.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
@@ -95,11 +96,19 @@ bool StackProtector::runOnFunction(Function &Fn) {
|
||||
Attribute Attr = Fn.getFnAttribute("stack-protector-buffer-size");
|
||||
if (Attr.isStringAttribute() &&
|
||||
Attr.getValueAsString().getAsInteger(10, SSPBufferSize))
|
||||
return false; // Invalid integer string
|
||||
return false; // Invalid integer string
|
||||
|
||||
if (!RequiresStackProtector())
|
||||
return false;
|
||||
|
||||
// TODO(etienneb): Functions with funclets are not correctly supported now.
|
||||
// Do nothing if this is funclet-based personality.
|
||||
if (Fn.hasPersonalityFn()) {
|
||||
EHPersonality Personality = classifyEHPersonality(Fn.getPersonalityFn());
|
||||
if (isFuncletEHPersonality(Personality))
|
||||
return false;
|
||||
}
|
||||
|
||||
++NumFunProtected;
|
||||
return InsertStackProtectors();
|
||||
}
|
||||
@@ -313,9 +322,9 @@ static bool CreatePrologue(Function *F, Module *M, ReturnInst *RI,
|
||||
PointerType *PtrTy = Type::getInt8PtrTy(RI->getContext());
|
||||
AI = B.CreateAlloca(PtrTy, nullptr, "StackGuardSlot");
|
||||
|
||||
Value *Guard = getStackGuard(TLI, M, B, &SupportsSelectionDAGSP);
|
||||
Value *GuardSlot = getStackGuard(TLI, M, B, &SupportsSelectionDAGSP);
|
||||
B.CreateCall(Intrinsic::getDeclaration(M, Intrinsic::stackprotector),
|
||||
{Guard, AI});
|
||||
{GuardSlot, AI});
|
||||
return SupportsSelectionDAGSP;
|
||||
}
|
||||
|
||||
@@ -336,12 +345,36 @@ bool StackProtector::InsertStackProtectors() {
|
||||
if (!RI)
|
||||
continue;
|
||||
|
||||
// Generate prologue instrumentation if not already generated.
|
||||
if (!HasPrologue) {
|
||||
HasPrologue = true;
|
||||
SupportsSelectionDAGSP &= CreatePrologue(F, M, RI, TLI, AI);
|
||||
}
|
||||
|
||||
if (!SupportsSelectionDAGSP) {
|
||||
// SelectionDAG based code generation. Nothing else needs to be done here.
|
||||
// The epilogue instrumentation is postponed to SelectionDAG.
|
||||
if (SupportsSelectionDAGSP)
|
||||
break;
|
||||
|
||||
// Set HasIRCheck to true, so that SelectionDAG will not generate its own
|
||||
// version. SelectionDAG called 'shouldEmitSDCheck' to check whether
|
||||
// instrumentation has already been generated.
|
||||
HasIRCheck = true;
|
||||
|
||||
// Generate epilogue instrumentation. The epilogue intrumentation can be
|
||||
// function-based or inlined depending on which mechanism the target is
|
||||
// providing.
|
||||
if (Value* GuardCheck = TLI->getSSPStackGuardCheck(*M)) {
|
||||
// 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});
|
||||
llvm::Function *Function = cast<llvm::Function>(GuardCheck);
|
||||
Call->setAttributes(Function->getAttributes());
|
||||
Call->setCallingConv(Function->getCallingConv());
|
||||
} else {
|
||||
// Generate the epilogue with inline instrumentation.
|
||||
// If we do not support SelectionDAG based tail calls, generate IR level
|
||||
// tail calls.
|
||||
//
|
||||
@@ -372,10 +405,6 @@ bool StackProtector::InsertStackProtectors() {
|
||||
// fail BB generated by the stack protector pseudo instruction.
|
||||
BasicBlock *FailBB = CreateFailBB();
|
||||
|
||||
// Set HasIRCheck to true, so that SelectionDAG will not generate its own
|
||||
// version.
|
||||
HasIRCheck = true;
|
||||
|
||||
// Split the basic block before the return instruction.
|
||||
BasicBlock *NewBB = BB->splitBasicBlock(RI->getIterator(), "SP_return");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user