Protection against stack-based memory corruption errors using SafeStack

This patch adds the safe stack instrumentation pass to LLVM, which separates
the program stack into a safe stack, which stores return addresses, register
spills, and local variables that are statically verified to be accessed
in a safe way, and the unsafe stack, which stores everything else. Such
separation makes it much harder for an attacker to corrupt objects on the
safe stack, including function pointers stored in spilled registers and
return addresses. You can find more information about the safe stack, as
well as other parts of or control-flow hijack protection technique in our
OSDI paper on code-pointer integrity (http://dslab.epfl.ch/pubs/cpi.pdf)
and our project website (http://levee.epfl.ch).

The overhead of our implementation of the safe stack is very close to zero
(0.01% on the Phoronix benchmarks). This is lower than the overhead of
stack cookies, which are supported by LLVM and are commonly used today,
yet the security guarantees of the safe stack are strictly stronger than
stack cookies. In some cases, the safe stack improves performance due to
better cache locality.

Our current implementation of the safe stack is stable and robust, we
used it to recompile multiple projects on Linux including Chromium, and
we also recompiled the entire FreeBSD user-space system and more than 100
packages. We ran unit tests on the FreeBSD system and many of the packages
and observed no errors caused by the safe stack. The safe stack is also fully
binary compatible with non-instrumented code and can be applied to parts of
a program selectively.

This patch is our implementation of the safe stack on top of LLVM. The
patches make the following changes:

- Add the safestack function attribute, similar to the ssp, sspstrong and
  sspreq attributes.

- Add the SafeStack instrumentation pass that applies the safe stack to all
  functions that have the safestack attribute. This pass moves all unsafe local
  variables to the unsafe stack with a separate stack pointer, whereas all
  safe variables remain on the regular stack that is managed by LLVM as usual.

- Invoke the pass as the last stage before code generation (at the same time
  the existing cookie-based stack protector pass is invoked).

- Add unit tests for the safe stack.

Original patch by Volodymyr Kuznetsov and others at the Dependable Systems
Lab at EPFL; updates and upstreaming by myself.

Differential Revision: http://reviews.llvm.org/D6094

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@239761 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Peter Collingbourne
2015-06-15 21:07:11 +00:00
parent 3aef7761ac
commit 7ffec838a2
48 changed files with 1417 additions and 4 deletions
+22
View File
@@ -0,0 +1,22 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Address-of local taken (j = &a)
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%retval = alloca i32, align 4
%a = alloca i32, align 4
%j = alloca i32*, align 8
store i32 0, i32* %retval
%0 = load i32, 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
}
@@ -0,0 +1,39 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; array of [16 x i8]
define void @foo(i8* %a) nounwind uwtable safestack {
entry:
; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
; CHECK: store i8* %[[USST]], i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[AADDR:.*]] = alloca i8*, align 8
%a.addr = alloca i8*, align 8
; CHECK: %[[BUFPTR:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
; CHECK: %[[BUFPTR2:.*]] = bitcast i8* %[[BUFPTR]] to [16 x i8]*
%buf = alloca [16 x i8], align 16
; CHECK: store i8* {{.*}}, i8** %[[AADDR]], align 8
store i8* %a, i8** %a.addr, align 8
; CHECK: %[[GEP:.*]] = getelementptr inbounds [16 x i8], [16 x i8]* %[[BUFPTR2]], i32 0, i32 0
%gep = getelementptr inbounds [16 x i8], [16 x i8]* %buf, i32 0, i32 0
; CHECK: %[[A2:.*]] = load i8*, i8** %[[AADDR]], align 8
%a2 = load i8*, i8** %a.addr, align 8
; CHECK: call i8* @strcpy(i8* %[[GEP]], i8* %[[A2]])
%call = call i8* @strcpy(i8* %gep, i8* %a2)
; CHECK: store i8* %[[USP]], i8** @__safestack_unsafe_stack_ptr
ret void
}
declare i8* @strcpy(i8*, i8*)
+38
View File
@@ -0,0 +1,38 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
; array [4 x i8]
; Requires protector.
define void @foo(i8* %a) nounwind uwtable safestack {
entry:
; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
; CHECK: store i8* %[[USST]], i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[AADDR:.*]] = alloca i8*, align 8
%a.addr = alloca i8*, align 8
; CHECK: %[[BUFPTR:.*]] = getelementptr i8, i8* %[[USP]], i32 -4
; CHECK: %[[BUFPTR2:.*]] = bitcast i8* %[[BUFPTR]] to [4 x i8]*
%buf = alloca [4 x i8], align 1
; CHECK: store i8* {{.*}}, i8** %[[AADDR]], align 8
store i8* %a, i8** %a.addr, align 8
; CHECK: %[[GEP:.*]] = getelementptr inbounds [4 x i8], [4 x i8]* %[[BUFPTR2]], i32 0, i32 0
%gep = getelementptr inbounds [4 x i8], [4 x i8]* %buf, i32 0, i32 0
; CHECK: %[[A2:.*]] = load i8*, i8** %[[AADDR]], align 8
%a2 = load i8*, i8** %a.addr, align 8
; CHECK: call i8* @strcpy(i8* %[[GEP]], i8* %[[A2]])
%call = call i8* @strcpy(i8* %gep, i8* %a2)
; CHECK: store i8* %[[USP]], i8** @__safestack_unsafe_stack_ptr
ret void
}
declare i8* @strcpy(i8*, i8*)
+20
View File
@@ -0,0 +1,20 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; no arrays / no nested arrays
; Requires no protector.
; CHECK-LABEL: @foo(
define void @foo(i8* %a) nounwind uwtable safestack {
entry:
; CHECK-NOT: __safestack_unsafe_stack_ptr
%a.addr = alloca i8*, align 8
store i8* %a, i8** %a.addr, align 8
%0 = load i8*, i8** %a.addr, align 8
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i8* %0)
ret void
}
declare i32 @printf(i8*, ...)
+17
View File
@@ -0,0 +1,17 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; PtrToInt/IntToPtr Cast
; Requires no protector.
; CHECK-LABEL: @foo(
define void @foo() nounwind uwtable safestack {
entry:
; CHECK-NOT: __safestack_unsafe_stack_ptr
%a = alloca i32, align 4
%0 = ptrtoint i32* %a to i64
%1 = inttoptr i64 %0 to i32*
ret void
}
@@ -0,0 +1,26 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.nest = type { %struct.pair, %struct.pair }
%struct.pair = type { i32, i32 }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Nested structure, no arrays, no address-of expressions.
; Verify that the resulting gep-of-gep does not incorrectly trigger
; a safe stack protector.
; safestack attribute
; Requires no protector.
; CHECK-LABEL: @foo(
define void @foo() nounwind uwtable safestack {
entry:
; CHECK-NOT: __safestack_unsafe_stack_ptr
%c = alloca %struct.nest, align 4
%b = getelementptr inbounds %struct.nest, %struct.nest* %c, i32 0, i32 1
%_a = getelementptr inbounds %struct.pair, %struct.pair* %b, i32 0, i32 0
%0 = load i32, i32* %_a, align 4
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %0)
ret void
}
declare i32 @printf(i8*, ...)
+20
View File
@@ -0,0 +1,20 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%class.A = type { [2 x i8] }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; [2 x i8] in a class
; safestack attribute
; Requires no protector.
; CHECK-LABEL: @foo(
define signext i8 @foo() nounwind uwtable safestack {
entry:
; CHECK-NOT: __safestack_unsafe_stack_ptr
%a = alloca %class.A, align 1
%array = getelementptr inbounds %class.A, %class.A* %a, i32 0, i32 0
%arrayidx = getelementptr inbounds [2 x i8], [2 x i8]* %array, i32 0, i64 0
%0 = load i8, i8* %arrayidx, align 1
ret i8 %0
}
@@ -0,0 +1,28 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%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] }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; [2 x i8] nested in several layers of structs and unions
; safestack attribute
; Requires no protector.
; CHECK-LABEL: @foo(
define signext i8 @foo() nounwind uwtable safestack {
entry:
; CHECK-NOT: __safestack_unsafe_stack_ptr
%x = alloca %struct.deep, align 1
%b = getelementptr inbounds %struct.deep, %struct.deep* %x, i32 0, i32 0
%c = bitcast %union.anon* %b to %struct.anon*
%d = getelementptr inbounds %struct.anon, %struct.anon* %c, i32 0, i32 0
%e = getelementptr inbounds %struct.anon.0, %struct.anon.0* %d, i32 0, i32 0
%array = bitcast %union.anon.1* %e to [2 x i8]*
%arrayidx = getelementptr inbounds [2 x i8], [2 x i8]* %array, i32 0, i64 0
%0 = load i8, i8* %arrayidx, align 1
ret i8 %0
}
@@ -0,0 +1,21 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Variable sized alloca
; safestack attribute
; Requires protector.
define void @foo(i32 %n) nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%n.addr = alloca i32, align 4
%a = alloca i32*, align 8
store i32 %n, i32* %n.addr, align 4
%0 = load i32, i32* %n.addr, align 4
%conv = sext i32 %0 to i64
%1 = alloca i8, i64 %conv
%2 = bitcast i8* %1 to i32*
store i32* %2, i32** %a, align 8
ret void
}
@@ -0,0 +1,23 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a pointer
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%a = alloca i32*, align 8
%b = alloca i32**, align 8
%call = call i32* @getp()
store i32* %call, i32** %a, align 8
store i32** %a, i32*** %b, align 8
%0 = load i32**, i32*** %b, align 8
call void @funcall2(i32** %0)
ret void
}
declare void @funcall2(i32**)
declare i32* @getp()
@@ -0,0 +1,23 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a local cast to a ptr of a different type
; (e.g., int a; ... ; float *b = &a;)
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%a = alloca i32, align 4
%b = alloca float*, align 8
store i32 0, i32* %a, align 4
%0 = bitcast i32* %a to float*
store float* %0, float** %b, align 8
%1 = load float*, float** %b, align 8
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), float* %1)
ret void
}
declare i32 @printf(i8*, ...)
@@ -0,0 +1,20 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a local cast to a ptr of a different type (optimized)
; (e.g., int a; ... ; float *b = &a;)
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%a = alloca i32, align 4
store i32 0, i32* %a, align 4
%0 = bitcast i32* %a to float*
call void @funfloat(float* %0) nounwind
ret void
}
declare void @funfloat(float*)
+16
View File
@@ -0,0 +1,16 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Passing addr-of to function call
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%b = alloca i32, align 4
call void @funcall(i32* %b) nounwind
ret void
}
declare void @funcall(i32*)
@@ -0,0 +1,24 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a casted pointer
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%a = alloca i32*, align 8
%b = alloca float**, align 8
%call = call i32* @getp()
store i32* %call, i32** %a, align 8
%0 = bitcast i32** %a to float**
store float** %0, float*** %b, align 8
%1 = load float**, float*** %b, align 8
call void @funfloat2(float** %1)
ret void
}
declare void @funfloat2(float**)
declare i32* @getp()
@@ -0,0 +1,20 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.pair = type { i32, i32 }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of struct element, GEP followed by callinst.
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%c = alloca %struct.pair, align 4
%y = getelementptr inbounds %struct.pair, %struct.pair* %c, i64 0, i32 1
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32* %y) nounwind
ret void
}
declare i32 @printf(i8*, ...)
@@ -0,0 +1,34 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.pair = type { i32, i32 }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a struct element passed into an invoke instruction.
; (GEP followed by an invoke)
; safestack attribute
; Requires protector.
define i32 @foo() uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%c = alloca %struct.pair, align 4
%exn.slot = alloca i8*
%ehselector.slot = alloca i32
%a = getelementptr inbounds %struct.pair, %struct.pair* %c, i32 0, i32 0
store i32 0, i32* %a, align 4
%a1 = getelementptr inbounds %struct.pair, %struct.pair* %c, i32 0, i32 0
invoke void @_Z3exceptPi(i32* %a1)
to label %invoke.cont unwind label %lpad
invoke.cont:
ret i32 0
lpad:
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
catch i8* null
ret i32 0
}
declare void @_Z3exceptPi(i32*)
declare i32 @__gxx_personality_v0(...)
@@ -0,0 +1,18 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a local, optimized into a GEP (e.g., &a - 12)
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%a = alloca i32, align 4
%add.ptr5 = getelementptr inbounds i32, i32* %a, i64 -12
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32* %add.ptr5) nounwind
ret void
}
declare i32 @printf(i8*, ...)
@@ -0,0 +1,22 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.pair = type { i32, i32 }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of struct element, GEP followed by ptrtoint.
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%c = alloca %struct.pair, align 4
%b = alloca i32*, align 8
%y = getelementptr inbounds %struct.pair, %struct.pair* %c, i32 0, i32 1
%0 = ptrtoint i32* %y to i64
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i64 %0)
ret void
}
declare i32 @printf(i8*, ...)
@@ -0,0 +1,23 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.pair = type { i32, i32 }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of struct element. (GEP followed by store).
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%c = alloca %struct.pair, align 4
%b = alloca i32*, align 8
%y = getelementptr inbounds %struct.pair, %struct.pair* %c, i32 0, i32 1
store i32* %y, i32** %b, align 8
%0 = load i32*, i32** %b, align 8
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32* %0)
ret void
}
declare i32 @printf(i8*, ...)
@@ -0,0 +1,36 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of in phi instruction
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%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], [4 x i8]* @.str, i64 0, i64 0), double* %y.0) nounwind
ret void
}
declare double @testi_aux()
declare i32 @printf(i8*, ...)
@@ -0,0 +1,22 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of in select instruction
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%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], [4 x i8]* @.str, i32 0, i32 0), double* %y.1)
ret void
}
declare double @testi_aux()
declare i32 @printf(i8*, ...)
@@ -0,0 +1,21 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.vec = type { <4 x i32> }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a vector nested in a struct
; safestack attribute
; Requires protector.
define void @foo() nounwind uwtable safestack {
entry:
; CHECK: __safestack_unsafe_stack_ptr
%c = alloca %struct.vec, align 16
%y = getelementptr inbounds %struct.vec, %struct.vec* %c, i64 0, i32 0
%add.ptr = getelementptr inbounds <4 x i32>, <4 x i32>* %y, i64 -12
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), <4 x i32>* %add.ptr) nounwind
ret void
}
declare i32 @printf(i8*, ...)
+33
View File
@@ -0,0 +1,33 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Addr-of a variable passed into an invoke instruction.
; safestack attribute
; Requires protector and stack restore after landing pad.
define i32 @foo() uwtable safestack {
entry:
; CHECK: %[[SP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[STATICTOP:.*]] = getelementptr i8, i8* %[[SP]], i32 -16
%a = alloca i32, align 4
%exn.slot = alloca i8*
%ehselector.slot = alloca i32
store i32 0, i32* %a, align 4
invoke void @_Z3exceptPi(i32* %a)
to label %invoke.cont unwind label %lpad
invoke.cont:
ret i32 0
lpad:
; CHECK: landingpad
; CHECK-NEXT: catch
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
catch i8* null
; CHECK-NEXT: store i8* %[[STATICTOP]], i8** @__safestack_unsafe_stack_ptr
ret i32 0
}
declare void @_Z3exceptPi(i32*)
declare i32 @__gxx_personality_v0(...)
+25
View File
@@ -0,0 +1,25 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; no safestack attribute
; Requires no protector.
; CHECK: @foo
define void @foo(i8* %a) nounwind uwtable {
entry:
; CHECK-NOT: __safestack_unsafe_stack_ptr
%a.addr = alloca i8*, align 8
%buf = alloca [16 x i8], align 16
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [16 x i8], [16 x i8]* %buf, i32 0, i32 0
%0 = load i8*, i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [16 x i8], [16 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
declare i8* @strcpy(i8*, i8*)
declare i32 @printf(i8*, ...)
+50
View File
@@ -0,0 +1,50 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.small = type { i8 }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; Address-of a structure taken in a function with a loop where
; the alloca is an incoming value to a PHI node and a use of that PHI
; node is also an incoming value.
; Verify that the address-of analysis does not get stuck in infinite
; recursion when chasing the alloca through the PHI nodes.
; Requires protector.
define i32 @foo(i32 %arg) nounwind uwtable safestack {
bb:
; CHECK: __safestack_unsafe_stack_ptr
%tmp = alloca %struct.small*, align 8
%tmp1 = call i32 (...) @dummy(%struct.small** %tmp) nounwind
%tmp2 = load %struct.small*, %struct.small** %tmp, align 8
%tmp3 = ptrtoint %struct.small* %tmp2 to i64
%tmp4 = trunc i64 %tmp3 to i32
%tmp5 = icmp sgt i32 %tmp4, 0
br i1 %tmp5, label %bb6, label %bb21
bb6: ; preds = %bb17, %bb
%tmp7 = phi %struct.small* [ %tmp19, %bb17 ], [ %tmp2, %bb ]
%tmp8 = phi i64 [ %tmp20, %bb17 ], [ 1, %bb ]
%tmp9 = phi i32 [ %tmp14, %bb17 ], [ %tmp1, %bb ]
%tmp10 = getelementptr inbounds %struct.small, %struct.small* %tmp7, i64 0, i32 0
%tmp11 = load i8, i8* %tmp10, align 1
%tmp12 = icmp eq i8 %tmp11, 1
%tmp13 = add nsw i32 %tmp9, 8
%tmp14 = select i1 %tmp12, i32 %tmp13, i32 %tmp9
%tmp15 = trunc i64 %tmp8 to i32
%tmp16 = icmp eq i32 %tmp15, %tmp4
br i1 %tmp16, label %bb21, label %bb17
bb17: ; preds = %bb6
%tmp18 = getelementptr inbounds %struct.small*, %struct.small** %tmp, i64 %tmp8
%tmp19 = load %struct.small*, %struct.small** %tmp18, align 8
%tmp20 = add i64 %tmp8, 1
br label %bb6
bb21: ; preds = %bb6, %bb
%tmp22 = phi i32 [ %tmp1, %bb ], [ %tmp14, %bb6 ]
%tmp23 = call i32 (...) @dummy(i32 %tmp22) nounwind
ret i32 undef
}
declare i32 @dummy(...)
+37
View File
@@ -0,0 +1,37 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t }
%struct.__sigset_t = type { [16 x i64] }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
@buf = internal global [1 x %struct.__jmp_buf_tag] zeroinitializer, align 16
; setjmp/longjmp test.
; Requires protector.
define i32 @foo() nounwind uwtable safestack {
entry:
; CHECK: %[[SP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[STATICTOP:.*]] = getelementptr i8, i8* %[[SP]], i32 -16
%retval = alloca i32, align 4
%x = alloca i32, align 4
store i32 0, i32* %retval
store i32 42, i32* %x, align 4
%call = call i32 @_setjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @buf, i32 0, i32 0)) returns_twice
; CHECK: setjmp
; CHECK-NEXT: store i8* %[[STATICTOP]], i8** @__safestack_unsafe_stack_ptr
%tobool = icmp ne i32 %call, 0
br i1 %tobool, label %if.else, label %if.then
if.then: ; preds = %entry
call void @funcall(i32* %x)
br label %if.end
if.else: ; preds = %entry
call i32 (...) @dummy()
br label %if.end
if.end: ; preds = %if.else, %if.then
ret i32 0
}
declare i32 @_setjmp(%struct.__jmp_buf_tag*)
declare void @funcall(i32*)
declare i32 @dummy(...)
+42
View File
@@ -0,0 +1,42 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t }
%struct.__sigset_t = type { [16 x i64] }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
@buf = internal global [1 x %struct.__jmp_buf_tag] zeroinitializer, align 16
; setjmp/longjmp test with dynamically sized array.
; Requires protector.
; CHECK: @foo(i32 %[[ARG:.*]])
define i32 @foo(i32 %size) nounwind uwtable safestack {
entry:
; CHECK: %[[DYNPTR:.*]] = alloca i8*
; CHECK-NEXT: %[[SP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
; CHECK-NEXT: store i8* %[[SP]], i8** %[[DYNPTR]]
; CHECK-NEXT: %[[ZEXT:.*]] = zext i32 %[[ARG]] to i64
; CHECK-NEXT: %[[MUL:.*]] = mul i64 %[[ZEXT]], 4
; CHECK-NEXT: %[[SP2:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
; CHECK-NEXT: %[[PTRTOINT:.*]] = ptrtoint i8* %[[SP2]] to i64
; CHECK-NEXT: %[[SUB:.*]] = sub i64 %[[PTRTOINT]], %[[MUL]]
; CHECK-NEXT: %[[AND:.*]] = and i64 %[[SUB]], -16
; CHECK-NEXT: %[[INTTOPTR:.*]] = inttoptr i64 %[[AND]] to i8*
; CHECK-NEXT: store i8* %[[INTTOPTR]], i8** @__safestack_unsafe_stack_ptr
; CHECK-NEXT: store i8* %[[INTTOPTR]], i8** %unsafe_stack_dynamic_ptr
; CHECK-NEXT: %[[ALLOCA:.*]] = inttoptr i64 %[[SUB]] to i32*
%a = alloca i32, i32 %size
; CHECK: setjmp
; CHECK-NEXT: %[[LOAD:.*]] = load i8*, i8** %[[DYNPTR]]
; CHECK-NEXT: store i8* %[[LOAD]], i8** @__safestack_unsafe_stack_ptr
%call = call i32 @_setjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @buf, i32 0, i32 0)) returns_twice
; CHECK: call void @funcall(i32* %[[ALLOCA]])
call void @funcall(i32* %a)
ret i32 0
}
declare i32 @_setjmp(%struct.__jmp_buf_tag*)
declare void @funcall(i32*)
+41
View File
@@ -0,0 +1,41 @@
; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s
; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
%struct.foo = type { [16 x i8] }
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; struct { [16 x i8] }
define void @foo(i8* %a) nounwind uwtable safestack {
entry:
; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
; CHECK: store i8* %[[USST]], i8** @__safestack_unsafe_stack_ptr
; CHECK: %[[AADDR:.*]] = alloca i8*, align 8
%a.addr = alloca i8*, align 8
; CHECK: %[[BUFPTR:.*]] = getelementptr i8, i8* %[[USP]], i32 -16
; CHECK: %[[BUFPTR2:.*]] = bitcast i8* %[[BUFPTR]] to %struct.foo*
%buf = alloca %struct.foo, align 1
; CHECK: store i8* {{.*}}, i8** %[[AADDR]], align 8
store i8* %a, i8** %a.addr, align 8
; CHECK: %[[GEP:.*]] = getelementptr inbounds %struct.foo, %struct.foo* %[[BUFPTR2]], i32 0, i32 0, i32 0
%gep = getelementptr inbounds %struct.foo, %struct.foo* %buf, i32 0, i32 0, i32 0
; CHECK: %[[A:.*]] = load i8*, i8** %[[AADDR]], align 8
%a2 = load i8*, i8** %a.addr, align 8
; CHECK: call i8* @strcpy(i8* %[[GEP]], i8* %[[A]])
%call = call i8* @strcpy(i8* %gep, i8* %a2)
; CHECK: store i8* %[[USP]], i8** @__safestack_unsafe_stack_ptr
ret void
}
declare i8* @strcpy(i8*, i8*)