aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/LangRef.rst17
-rw-r--r--lib/CodeGen/StackProtector.cpp126
-rw-r--r--test/CodeGen/X86/stack-protector.ll2530
3 files changed, 2635 insertions, 38 deletions
diff --git a/docs/LangRef.rst b/docs/LangRef.rst
index 1a85469550..4fd4c3ee27 100644
--- a/docs/LangRef.rst
+++ b/docs/LangRef.rst
@@ -825,7 +825,11 @@ example:
placed on the stack before the local variables that's checked upon
return from the function to see if it has been overwritten. A
heuristic is used to determine if a function needs stack protectors
- or not.
+ or not. The heuristic used will enable protectors for functions with:
+ - Character arrays larger than ``ssp-buffer-size`` (default 8).
+ - Aggregates containing character arrays larger than ``ssp-buffer-size``.
+ - Calls to alloca() with variable sizes or constant sizes greater than
+ ``ssp-buffer-size``.
If a function that has an ``ssp`` attribute is inlined into a
function that doesn't have an ``ssp`` attribute, then the resulting
@@ -841,8 +845,15 @@ example:
an ``sspreq`` attribute.
``sspstrong``
This attribute indicates that the function should emit a stack smashing
- protector. Currently this attribute has the same effect as
- ``sspreq``. This overrides the ``ssp`` function attribute.
+ protector. This attribute causes a strong heuristic to be used when
+ determining if a function needs stack protectors. The strong heuristic
+ will enable protectors for functions with:
+ - Arrays of any size and type
+ - Aggregates containing an array of any size and type.
+ - Calls to alloca().
+ - Local variables that have had their address taken.
+
+ This overrides the ``ssp`` function attribute.
If a function that has an ``sspstrong`` attribute is inlined into a
function that doesn't have an ``sspstrong`` attribute, then the
diff --git a/lib/CodeGen/StackProtector.cpp b/lib/CodeGen/StackProtector.cpp
index 049efc1c86..f3be37c9ee 100644
--- a/lib/CodeGen/StackProtector.cpp
+++ b/lib/CodeGen/StackProtector.cpp
@@ -16,6 +16,8 @@
#define DEBUG_TYPE "stack-protector"
#include "llvm/CodeGen/Passes.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/IR/Attributes.h"
@@ -32,6 +34,10 @@
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
+STATISTIC(NumFunProtected, "Number of functions protected");
+STATISTIC(NumAddrTaken, "Number of local variables that have their address"
+ " taken.");
+
namespace {
class StackProtector : public FunctionPass {
/// TLI - Keep a pointer of a TargetLowering to consult for determining
@@ -43,6 +49,12 @@ namespace {
DominatorTree *DT;
+ /// VisitedPHIs - The set of PHI nodes visited when determining
+ /// if a variable's reference has been taken. This set
+ /// is maintained to ensure we don't visit the same PHI node multiple
+ /// times.
+ SmallPtrSet<const PHINode*, 16> VisitedPHIs;
+
/// InsertStackProtectors - Insert code into the prologue and epilogue of
/// the function.
///
@@ -58,11 +70,15 @@ namespace {
/// ContainsProtectableArray - Check whether the type either is an array or
/// contains an array of sufficient size so that we need stack protectors
/// for it.
- bool ContainsProtectableArray(Type *Ty, bool InStruct = false) const;
+ bool ContainsProtectableArray(Type *Ty, bool Strong = false,
+ bool InStruct = false) const;
+
+ /// \brief Check whether a stack allocation has its address taken.
+ bool HasAddressTaken(const Instruction *AI);
/// RequiresStackProtector - Check whether or not this function needs a
/// stack protector based upon the stack protector level.
- bool RequiresStackProtector() const;
+ bool RequiresStackProtector();
public:
static char ID; // Pass identification, replacement for typeid.
StackProtector() : FunctionPass(ID), TLI(0) {
@@ -96,15 +112,21 @@ bool StackProtector::runOnFunction(Function &Fn) {
if (!RequiresStackProtector()) return false;
+ ++NumFunProtected;
return InsertStackProtectors();
}
/// ContainsProtectableArray - Check whether the type either is an array or
/// contains a char array of sufficient size so that we need stack protectors
/// for it.
-bool StackProtector::ContainsProtectableArray(Type *Ty, bool InStruct) const {
+bool StackProtector::ContainsProtectableArray(Type *Ty, bool Strong,
+ bool InStruct) const {
if (!Ty) return false;
if (ArrayType *AT = dyn_cast<ArrayType>(Ty)) {
+ // In strong mode any array, regardless of type and size, triggers a
+ // protector
+ if (Strong)
+ return true;
const TargetMachine &TM = TLI->getTargetMachine();
if (!AT->getElementType()->isIntegerTy(8)) {
Triple Trip(TM.getTargetTriple());
@@ -126,45 +148,103 @@ bool StackProtector::ContainsProtectableArray(Type *Ty, bool InStruct) const {
for (StructType::element_iterator I = ST->element_begin(),
E = ST->element_end(); I != E; ++I)
- if (ContainsProtectableArray(*I, true))
+ if (ContainsProtectableArray(*I, Strong, true))
return true;
return false;
}
-/// RequiresStackProtector - Check whether or not this function needs a stack
-/// protector based upon the stack protector level. The heuristic we use is to
-/// add a guard variable to functions that call alloca, and functions with
-/// buffers larger than SSPBufferSize bytes.
-bool StackProtector::RequiresStackProtector() const {
- if (F->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
- Attribute::StackProtectReq))
- return true;
+bool StackProtector::HasAddressTaken(const Instruction *AI) {
+ for (Value::const_use_iterator UI = AI->use_begin(), UE = AI->use_end();
+ UI != UE; ++UI) {
+ const User *U = *UI;
+ if (const StoreInst *SI = dyn_cast<StoreInst>(U)) {
+ if (AI == SI->getValueOperand())
+ return true;
+ } else if (const PtrToIntInst *SI = dyn_cast<PtrToIntInst>(U)) {
+ if (AI == SI->getOperand(0))
+ return true;
+ } else if (isa<CallInst>(U)) {
+ return true;
+ } else if (isa<InvokeInst>(U)) {
+ return true;
+ } else if (const SelectInst *SI = dyn_cast<SelectInst>(U)) {
+ if (HasAddressTaken(SI))
+ return true;
+ } else if (const PHINode *PN = dyn_cast<PHINode>(U)) {
+ // Keep track of what PHI nodes we have already visited to ensure
+ // they are only visited once.
+ if (VisitedPHIs.insert(PN))
+ if (HasAddressTaken(PN))
+ return true;
+ } else if (const GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(U)) {
+ if (HasAddressTaken(GEP))
+ return true;
+ } else if (const BitCastInst *BI = dyn_cast<BitCastInst>(U)) {
+ if (HasAddressTaken(BI))
+ return true;
+ }
+ }
+ return false;
+}
- // FIXME: Dummy SSP-strong implementation. Default to required until
- // strong heuristic is implemented.
+/// \brief Check whether or not this function needs a stack protector based
+/// upon the stack protector level.
+///
+/// We use two heuristics: a standard (ssp) and strong (sspstrong).
+/// The standard heuristic which will add a guard variable to functions that
+/// call alloca with a either a variable size or a size >= SSPBufferSize,
+/// functions with character buffers larger than SSPBufferSize, and functions
+/// with aggregates containing character buffers larger than SSPBufferSize. The
+/// strong heuristic will add a guard variables to functions that call alloca
+/// regardless of size, functions with any buffer regardless of type and size,
+/// functions with aggregates that contain any buffer regardless of type and
+/// size, and functions that contain stack-based variables that have had their
+/// address taken.
+bool StackProtector::RequiresStackProtector() {
+ bool Strong = false;
if (F->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
- Attribute::StackProtectStrong))
+ Attribute::StackProtectReq))
return true;
-
- if (!F->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
- Attribute::StackProtect))
+ else if (F->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
+ Attribute::StackProtectStrong))
+ Strong = true;
+ else if (!F->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
+ Attribute::StackProtect))
return false;
for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) {
BasicBlock *BB = I;
for (BasicBlock::iterator
- II = BB->begin(), IE = BB->end(); II != IE; ++II)
+ II = BB->begin(), IE = BB->end(); II != IE; ++II) {
if (AllocaInst *AI = dyn_cast<AllocaInst>(II)) {
- if (AI->isArrayAllocation())
- // This is a call to alloca with a variable size. Emit stack
- // protectors.
+ if (AI->isArrayAllocation()) {
+ // SSP-Strong: Enable protectors for any call to alloca, regardless
+ // of size.
+ if (Strong)
+ return true;
+
+ if (const ConstantInt *CI =
+ dyn_cast<ConstantInt>(AI->getArraySize())) {
+ unsigned BufferSize = TLI->getTargetMachine().Options.SSPBufferSize;
+ if (CI->getLimitedValue(BufferSize) >= BufferSize)
+ // A call to alloca with size >= SSPBufferSize requires
+ // stack protectors.
+ return true;
+ } else // A call to alloca with a variable size requires protectors.
+ return true;
+ }
+
+ if (ContainsProtectableArray(AI->getAllocatedType(), Strong))
return true;
- if (ContainsProtectableArray(AI->getAllocatedType()))
+ if (Strong && HasAddressTaken(AI)) {
+ ++NumAddrTaken;
return true;
+ }
}
+ }
}
return false;
diff --git a/test/CodeGen/X86/stack-protector.ll b/test/CodeGen/X86/stack-protector.ll
index afe5ab81bc..1e9ca1d2c2 100644
--- a/test/CodeGen/X86/stack-protector.ll
+++ b/test/CodeGen/X86/stack-protector.ll
@@ -2,10 +2,19 @@
; RUN: llc -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck --check-prefix=LINUX-X64 %s
; RUN: llc -code-model=kernel -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck --check-prefix=LINUX-KERNEL-X64 %s
; RUN: llc -mtriple=x86_64-apple-darwin < %s -o - | FileCheck --check-prefix=DARWIN-X64 %s
-; FIXME: Update and expand test when strong heuristic is implemented.
%struct.foo = type { [16 x i8] }
%struct.foo.0 = type { [4 x i8] }
+%struct.pair = type { i32, i32 }
+%struct.nest = type { %struct.pair, %struct.pair }
+%struct.vec = type { <4 x i32> }
+%class.A = type { [2 x i8] }
+%struct.deep = type { %union.anon }
+%union.anon = type { %struct.anon }
+%struct.anon = type { %struct.anon.0 }
+%struct.anon.0 = type { %union.anon.1 }
+%union.anon.1 = type { [2 x i8] }
+%struct.small = type { i8 }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
@@ -577,26 +586,24 @@ entry:
; test5c: no arrays / no nested arrays
; sspstrong attribute
-; Requires protector.
-; FIXME: Once strong heuristic is implemented, this should _not_ require
-; a protector
+; Requires no protector.
define void @test5c(i8* %a) nounwind uwtable sspstrong {
entry:
; LINUX-I386: test5c:
-; LINUX-I386: mov{{l|q}} %gs:
-; LINUX-I386: calll __stack_chk_fail
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
; LINUX-X64: test5c:
-; LINUX-X64: mov{{l|q}} %fs:
-; LINUX-X64: callq __stack_chk_fail
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test5c:
-; LINUX-KERNEL-X64: mov{{l|q}} %gs:
-; LINUX-KERNEL-X64: callq __stack_chk_fail
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test5c:
-; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
-; DARWIN-X64: callq ___stack_chk_fail
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
store i8* %a, i8** %a.addr, align 8
%0 = load i8** %a.addr, align 8
@@ -631,5 +638,2504 @@ entry:
ret void
}
+; test6a: Address-of local taken (j = &a)
+; no ssp attribute
+; Requires no protector.
+define void @test6a() nounwind uwtable {
+entry:
+; LINUX-I386: test6a:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test6a:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test6a:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test6a:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %retval = alloca i32, align 4
+ %a = alloca i32, align 4
+ %j = alloca i32*, align 8
+ store i32 0, i32* %retval
+ %0 = load i32* %a, align 4
+ %add = add nsw i32 %0, 1
+ store i32 %add, i32* %a, align 4
+ store i32* %a, i32** %j, align 8
+ ret void
+}
+
+; test6b: Address-of local taken (j = &a)
+; ssp attribute
+; Requires no protector.
+define void @test6b() nounwind uwtable ssp {
+entry:
+; LINUX-I386: test6b:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test6b:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test6b:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test6b:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %retval = alloca i32, align 4
+ %a = alloca i32, align 4
+ %j = alloca i32*, align 8
+ store i32 0, i32* %retval
+ %0 = load i32* %a, align 4
+ %add = add nsw i32 %0, 1
+ store i32 %add, i32* %a, align 4
+ store i32* %a, i32** %j, align 8
+ ret void
+}
+
+; test6c: Address-of local taken (j = &a)
+; sspstrong attribute
+; Requires protector.
+define void @test6c() nounwind uwtable sspstrong {
+entry:
+; LINUX-I386: test6c:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test6c:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test6c:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test6c:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %retval = alloca i32, align 4
+ %a = alloca i32, align 4
+ %j = alloca i32*, align 8
+ store i32 0, i32* %retval
+ %0 = load i32* %a, align 4
+ %add = add nsw i32 %0, 1
+ store i32 %add, i32* %a, align 4
+ store i32* %a, i32** %j, align 8
+ ret void
+}
+
+; test6d: Address-of local taken (j = &a)
+; sspreq attribute
+; Requires protector.
+define void @test6d() nounwind uwtable sspreq {
+entry:
+; LINUX-I386: test6d:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test6d:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test6d:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test6d:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %retval = alloca i32, align 4
+ %a = alloca i32, align 4
+ %j = alloca i32*, align 8
+ store i32 0, i32* %retval
+ %0 = load i32* %a, align 4
+ %add = add nsw i32 %0, 1
+ store i32 %add, i32* %a, align 4
+ store i32* %a, i32** %j, align 8
+ ret void
+}
+
+; test7a: PtrToInt Cast
+; no ssp attribute
+; Requires no protector.
+define void @test7a() nounwind uwtable readnone {
+entry:
+; LINUX-I386: test7a:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test7a:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test7a:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test7a:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %a = alloca i32, align 4
+ %0 = ptrtoint i32* %a to i64
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i64 %0)
+ ret void
+}
+
+; test7b: PtrToInt Cast
+; ssp attribute
+; Requires no protector.
+define void @test7b() nounwind uwtable readnone ssp {
+entry:
+; LINUX-I386: test7b:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test7b:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test7b:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test7b:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %a = alloca i32, align 4
+ %0 = ptrtoint i32* %a to i64
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i64 %0)
+ ret void
+}
+
+; test7c: PtrToInt Cast
+; sspstrong attribute
+; Requires protector.
+define void @test7c() nounwind uwtable readnone sspstrong {
+entry:
+; LINUX-I386: test7c:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test7c:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test7c:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test7c:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %a = alloca i32, align 4
+ %0 = ptrtoint i32* %a to i64
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i64 %0)
+ ret void
+}
+
+; test7d: PtrToInt Cast
+; sspreq attribute
+; Requires protector.
+define void @test7d() nounwind uwtable readnone sspreq {
+entry:
+; LINUX-I386: test7d:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test7d:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test7d:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test7d:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %a = alloca i32, align 4
+ %0 = ptrtoint i32* %a to i64
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i64 %0)
+ ret void
+}
+
+; test8a: Passing addr-of to function call
+; no ssp attribute
+; Requires no protector.
+define void @test8a() nounwind uwtable {
+entry:
+; LINUX-I386: test8a:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test8a:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test8a:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test8a:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %b = alloca i32, align 4
+ call void @funcall(i32* %b) nounwind
+ ret void
+}
+
+; test8b: Passing addr-of to function call
+; ssp attribute
+; Requires no protector.
+define void @test8b() nounwind uwtable ssp {
+entry:
+; LINUX-I386: test8b:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test8b:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test8b:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test8b:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %b = alloca i32, align 4
+ call void @funcall(i32* %b) nounwind
+ ret void
+}
+
+; test8c: Passing addr-of to function call
+; sspstrong attribute
+; Requires protector.
+define void @test8c() nounwind uwtable sspstrong {
+entry:
+; LINUX-I386: test8c:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test8c:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test8c:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test8c:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %b = alloca i32, align 4
+ call void @funcall(i32* %b) nounwind
+ ret void
+}
+
+; test8d: Passing addr-of to function call
+; sspreq attribute
+; Requires protector.
+define void @test8d() nounwind uwtable sspreq {
+entry:
+; LINUX-I386: test8d:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test8d:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test8d:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test8d:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %b = alloca i32, align 4
+ call void @funcall(i32* %b) nounwind
+ ret void
+}
+
+; test9a: Addr-of in select instruction
+; no ssp attribute
+; Requires no protector.
+define void @test9a() nounwind uwtable {
+entry:
+; LINUX-I386: test9a:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test9a:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test9a:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test9a:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp2 = fcmp ogt double %call, 0.000000e+00
+ %y.1 = select i1 %cmp2, double* %x, double* null
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), double* %y.1)
+ ret void
+}
+
+; test9b: Addr-of in select instruction
+; ssp attribute
+; Requires no protector.
+define void @test9b() nounwind uwtable ssp {
+entry:
+; LINUX-I386: test9b:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test9b:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test9b:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test9b:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp2 = fcmp ogt double %call, 0.000000e+00
+ %y.1 = select i1 %cmp2, double* %x, double* null
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), double* %y.1)
+ ret void
+}
+
+; test9c: Addr-of in select instruction
+; sspstrong attribute
+; Requires protector.
+define void @test9c() nounwind uwtable sspstrong {
+entry:
+; LINUX-I386: test9c:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test9c:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test9c:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test9c:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp2 = fcmp ogt double %call, 0.000000e+00
+ %y.1 = select i1 %cmp2, double* %x, double* null
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), double* %y.1)
+ ret void
+}
+
+; test9d: Addr-of in select instruction
+; sspreq attribute
+; Requires protector.
+define void @test9d() nounwind uwtable sspreq {
+entry:
+; LINUX-I386: test9d:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test9d:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test9d:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test9d:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp2 = fcmp ogt double %call, 0.000000e+00
+ %y.1 = select i1 %cmp2, double* %x, double* null
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), double* %y.1)
+ ret void
+}
+
+; test10a: Addr-of in phi instruction
+; no ssp attribute
+; Requires no protector.
+define void @test10a() nounwind uwtable {
+entry:
+; LINUX-I386: test10a:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test10a:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test10a:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test10a:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp = fcmp ogt double %call, 3.140000e+00
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call1 = call double @testi_aux() nounwind
+ store double %call1, double* %x, align 8
+ br label %if.end4
+
+if.else: ; preds = %entry
+ %cmp2 = fcmp ogt double %call, 1.000000e+00
+ br i1 %cmp2, label %if.then3, label %if.end4
+
+if.then3: ; preds = %if.else
+ br label %if.end4
+
+if.end4: ; preds = %if.else, %if.then3, %if.then
+ %y.0 = phi double* [ null, %if.then ], [ %x, %if.then3 ], [ null, %if.else ]
+ %call5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), double* %y.0) nounwind
+ ret void
+}
+
+; test10b: Addr-of in phi instruction
+; ssp attribute
+; Requires no protector.
+define void @test10b() nounwind uwtable ssp {
+entry:
+; LINUX-I386: test10b:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test10b:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test10b:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test10b:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp = fcmp ogt double %call, 3.140000e+00
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call1 = call double @testi_aux() nounwind
+ store double %call1, double* %x, align 8
+ br label %if.end4
+
+if.else: ; preds = %entry
+ %cmp2 = fcmp ogt double %call, 1.000000e+00
+ br i1 %cmp2, label %if.then3, label %if.end4
+
+if.then3: ; preds = %if.else
+ br label %if.end4
+
+if.end4: ; preds = %if.else, %if.then3, %if.then
+ %y.0 = phi double* [ null, %if.then ], [ %x, %if.then3 ], [ null, %if.else ]
+ %call5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), double* %y.0) nounwind
+ ret void
+}
+
+; test10c: Addr-of in phi instruction
+; sspstrong attribute
+; Requires protector.
+define void @test10c() nounwind uwtable sspstrong {
+entry:
+; LINUX-I386: test10c:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test10c:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test10c:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test10c:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp = fcmp ogt double %call, 3.140000e+00
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call1 = call double @testi_aux() nounwind
+ store double %call1, double* %x, align 8
+ br label %if.end4
+
+if.else: ; preds = %entry
+ %cmp2 = fcmp ogt double %call, 1.000000e+00
+ br i1 %cmp2, label %if.then3, label %if.end4
+
+if.then3: ; preds = %if.else
+ br label %if.end4
+
+if.end4: ; preds = %if.else, %if.then3, %if.then
+ %y.0 = phi double* [ null, %if.then ], [ %x, %if.then3 ], [ null, %if.else ]
+ %call5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), double* %y.0) nounwind
+ ret void
+}
+
+; test10d: Addr-of in phi instruction
+; sspreq attribute
+; Requires protector.
+define void @test10d() nounwind uwtable sspreq {
+entry:
+; LINUX-I386: test10d:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test10d:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test10d:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test10d:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %x = alloca double, align 8
+ %call = call double @testi_aux() nounwind
+ store double %call, double* %x, align 8
+ %cmp = fcmp ogt double %call, 3.140000e+00
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call1 = call double @testi_aux() nounwind
+ store double %call1, double* %x, align 8
+ br label %if.end4
+
+if.else: ; preds = %entry
+ %cmp2 = fcmp ogt double %call, 1.000000e+00
+ br i1 %cmp2, label %if.then3, label %if.end4
+
+if.then3: ; preds = %if.else
+ br label %if.end4
+
+if.end4: ; preds = %if.else, %if.then3, %if.then
+ %y.0 = phi double* [ null, %if.then ], [ %x, %if.then3 ], [ null, %if.else ]
+ %call5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), double* %y.0) nounwind
+ ret void
+}
+
+; test11a: Addr-of struct element. (GEP followed by store).
+; no ssp attribute
+; Requires no protector.
+define void @test11a() nounwind uwtable {
+entry:
+; LINUX-I386: test11a:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test11a:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test11a:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test11a:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %c = alloca %struct.pair, align 4
+ %b = alloca i32*, align 8
+ %y = getelementptr inbounds %struct.pair* %c, i32 0, i32 1
+ store i32* %y, i32** %b, align 8
+ %0 = load i32** %b, align 8
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32* %0)
+ ret void
+}
+
+; test11b: Addr-of struct element. (GEP followed by store).
+; ssp attribute
+; Requires no protector.
+define void @test11b() nounwind uwtable ssp {
+entry:
+; LINUX-I386: test11b:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc
+
+; LINUX-X64: test11b:
+; LINUX-X64-NOT: callq __stack_chk_fail
+; LINUX-X64: .cfi_endproc
+
+; LINUX-KERNEL-X64: test11b:
+; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
+; LINUX-KERNEL-X64: .cfi_endproc
+
+; DARWIN-X64: test11b:
+; DARWIN-X64-NOT: callq ___stack_chk_fail
+; DARWIN-X64: .cfi_endproc
+ %c = alloca %struct.pair, align 4
+ %b = alloca i32*, align 8
+ %y = getelementptr inbounds %struct.pair* %c, i32 0, i32 1
+ store i32* %y, i32** %b, align 8
+ %0 = load i32** %b, align 8
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32* %0)
+ ret void
+}
+
+; test11c: Addr-of struct element. (GEP followed by store).
+; sspstrong attribute
+; Requires protector.
+define void @test11c() nounwind uwtable sspstrong {
+entry:
+; LINUX-I386: test11c:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test11c:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test11c:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test11c:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %c = alloca %struct.pair, align 4
+ %b = alloca i32*, align 8
+ %y = getelementptr inbounds %struct.pair* %c, i32 0, i32 1
+ store i32* %y, i32** %b, align 8
+ %0 = load i32** %b, align 8
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32* %0)
+ ret void
+}
+
+; test11d: Addr-of struct element. (GEP followed by store).
+; sspreq attribute
+; Requires protector.
+define void @test11d() nounwind uwtable sspreq {
+entry:
+; LINUX-I386: test11d:
+; LINUX-I386: mov{{l|q}} %gs:
+; LINUX-I386: calll __stack_chk_fail
+
+; LINUX-X64: test11d:
+; LINUX-X64: mov{{l|q}} %fs:
+; LINUX-X64: callq __stack_chk_fail
+
+; LINUX-KERNEL-X64: test11d:
+; LINUX-KERNEL-X64: mov{{l|q}} %gs:
+; LINUX-KERNEL-X64: callq __stack_chk_fail
+
+; DARWIN-X64: test11d:
+; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
+; DARWIN-X64: callq ___stack_chk_fail
+ %c = alloca %struct.pair, align 4
+ %b = alloca i32*, align 8
+ %y = getelementptr inbounds %struct.pair* %c, i32 0, i32 1
+ store i32* %y, i32** %b, align 8
+ %0 = load i32** %b, align 8
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32* %0)
+ ret void
+}
+
+; test12a: Addr-of struct element, GEP followed by ptrtoint.
+; no ssp attribute
+; Requires no protector.
+define void @test12a() nounwind uwtable {
+entry:
+; LINUX-I386: test12a:
+; LINUX-I386-NOT: calll __stack_chk_fail
+; LINUX-I386: .cfi_endproc