Skip to content

Commit 28c1f51

Browse files
pogo59tstellar
authored andcommitted
Merging r373220:
------------------------------------------------------------------------ r373220 | probinson | 2019-09-30 08:11:23 -0700 (Mon, 30 Sep 2019) | 12 lines [SSP] [3/3] cmpxchg and addrspacecast instructions can now trigger stack protectors. Fixes PR42238. Add test coverage for llvm.memset, as proxy for all llvm.mem* intrinsics. There are two issues here: (1) they could be lowered to a libc call, which could be intercepted, and do Bad Stuff; (2) with a non-constant size, they could overwrite the current stack frame. The test was mostly written by Matt Arsenault in r363169, which was later reverted; I tweaked what he had and added the llvm.memset part. Differential Revision: https://reviews.llvm.org/D67845 ------------------------------------------------------------------------
1 parent 76817ab commit 28c1f51

File tree

2 files changed

+186
-2
lines changed

2 files changed

+186
-2
lines changed

llvm/lib/CodeGen/StackProtector.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,19 @@ bool StackProtector::HasAddressTaken(const Instruction *AI,
165165
if (AI == cast<StoreInst>(I)->getValueOperand())
166166
return true;
167167
break;
168+
case Instruction::AtomicCmpXchg:
169+
// cmpxchg conceptually includes both a load and store from the same
170+
// location. So, like store, the value being stored is what matters.
171+
if (AI == cast<AtomicCmpXchgInst>(I)->getNewValOperand())
172+
return true;
173+
break;
168174
case Instruction::PtrToInt:
169175
if (AI == cast<PtrToIntInst>(I)->getOperand(0))
170176
return true;
171177
break;
172178
case Instruction::Call: {
173-
// Ignore intrinsics that are not calls. TODO: Use isLoweredToCall().
179+
// Ignore intrinsics that do not become real instructions.
180+
// TODO: Narrow this to intrinsics that have store-like effects.
174181
const auto *CI = cast<CallInst>(I);
175182
if (!isa<DbgInfoIntrinsic>(CI) && !CI->isLifetimeStartOrEnd())
176183
return true;
@@ -181,6 +188,7 @@ bool StackProtector::HasAddressTaken(const Instruction *AI,
181188
case Instruction::BitCast:
182189
case Instruction::GetElementPtr:
183190
case Instruction::Select:
191+
case Instruction::AddrSpaceCast:
184192
if (HasAddressTaken(I, VisitedPHIs))
185193
return true;
186194
break;
@@ -193,8 +201,19 @@ bool StackProtector::HasAddressTaken(const Instruction *AI,
193201
return true;
194202
break;
195203
}
196-
default:
204+
case Instruction::Load:
205+
case Instruction::AtomicRMW:
206+
case Instruction::Ret:
207+
// These instructions take an address operand, but have load-like or
208+
// other innocuous behavior that should not trigger a stack protector.
209+
// atomicrmw conceptually has both load and store semantics, but the
210+
// value being stored must be integer; so if a pointer is being stored,
211+
// we'll catch it in the PtrToInt case above.
197212
break;
213+
default:
214+
// Conservatively return true for any instruction that takes an address
215+
// operand, but is not handled above.
216+
return true;
198217
}
199218
}
200219
return false;
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
; RUN: llc -mtriple=x86_64-pc-linux-gnu -start-before=stack-protector -stop-after=stack-protector -o - < %s | FileCheck %s
2+
; Bugs 42238/43308: Test some additional situations not caught previously.
3+
4+
define void @store_captures() #0 {
5+
; CHECK-LABEL: @store_captures(
6+
; CHECK-NEXT: entry:
7+
; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca i8*
8+
; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
9+
; CHECK-NEXT: call void @llvm.stackprotector(i8* [[STACKGUARD]], i8** [[STACKGUARDSLOT]])
10+
; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
11+
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
12+
; CHECK-NEXT: [[J:%.*]] = alloca i32*, align 8
13+
; CHECK-NEXT: store i32 0, i32* [[RETVAL]]
14+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[A]], align 4
15+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LOAD]], 1
16+
; CHECK-NEXT: store i32 [[ADD]], i32* [[A]], align 4
17+
; CHECK-NEXT: store i32* [[A]], i32** [[J]], align 8
18+
; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
19+
; CHECK-NEXT: [[TMP0:%.*]] = load volatile i8*, i8** [[STACKGUARDSLOT]]
20+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8* [[STACKGUARD1]], [[TMP0]]
21+
; CHECK-NEXT: br i1 [[TMP1]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0
22+
; CHECK: SP_return:
23+
; CHECK-NEXT: ret void
24+
; CHECK: CallStackCheckFailBlk:
25+
; CHECK-NEXT: call void @__stack_chk_fail()
26+
; CHECK-NEXT: unreachable
27+
;
28+
entry:
29+
%retval = alloca i32, align 4
30+
%a = alloca i32, align 4
31+
%j = alloca i32*, align 8
32+
store i32 0, i32* %retval
33+
%load = load i32, i32* %a, align 4
34+
%add = add nsw i32 %load, 1
35+
store i32 %add, i32* %a, align 4
36+
store i32* %a, i32** %j, align 8
37+
ret void
38+
}
39+
40+
define i32* @non_captures() #0 {
41+
; load, atomicrmw, and ret do not trigger a stack protector.
42+
; CHECK-LABEL: @non_captures(
43+
; CHECK-NEXT: entry:
44+
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
45+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[A]], align 4
46+
; CHECK-NEXT: [[ATOM:%.*]] = atomicrmw add i32* [[A]], i32 1 seq_cst
47+
; CHECK-NEXT: ret i32* [[A]]
48+
;
49+
entry:
50+
%a = alloca i32, align 4
51+
%load = load i32, i32* %a, align 4
52+
%atom = atomicrmw add i32* %a, i32 1 seq_cst
53+
ret i32* %a
54+
}
55+
56+
define void @store_addrspacecast_captures() #0 {
57+
; CHECK-LABEL: @store_addrspacecast_captures(
58+
; CHECK-NEXT: entry:
59+
; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca i8*
60+
; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
61+
; CHECK-NEXT: call void @llvm.stackprotector(i8* [[STACKGUARD]], i8** [[STACKGUARDSLOT]])
62+
; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
63+
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
64+
; CHECK-NEXT: [[J:%.*]] = alloca i32 addrspace(1)*, align 8
65+
; CHECK-NEXT: store i32 0, i32* [[RETVAL]]
66+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[A]], align 4
67+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LOAD]], 1
68+
; CHECK-NEXT: store i32 [[ADD]], i32* [[A]], align 4
69+
; CHECK-NEXT: [[A_ADDRSPACECAST:%.*]] = addrspacecast i32* [[A]] to i32 addrspace(1)*
70+
; CHECK-NEXT: store i32 addrspace(1)* [[A_ADDRSPACECAST]], i32 addrspace(1)** [[J]], align 8
71+
; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
72+
; CHECK-NEXT: [[TMP0:%.*]] = load volatile i8*, i8** [[STACKGUARDSLOT]]
73+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8* [[STACKGUARD1]], [[TMP0]]
74+
; CHECK-NEXT: br i1 [[TMP1]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0
75+
; CHECK: SP_return:
76+
; CHECK-NEXT: ret void
77+
; CHECK: CallStackCheckFailBlk:
78+
; CHECK-NEXT: call void @__stack_chk_fail()
79+
; CHECK-NEXT: unreachable
80+
;
81+
entry:
82+
%retval = alloca i32, align 4
83+
%a = alloca i32, align 4
84+
%j = alloca i32 addrspace(1)*, align 8
85+
store i32 0, i32* %retval
86+
%load = load i32, i32* %a, align 4
87+
%add = add nsw i32 %load, 1
88+
store i32 %add, i32* %a, align 4
89+
%a.addrspacecast = addrspacecast i32* %a to i32 addrspace(1)*
90+
store i32 addrspace(1)* %a.addrspacecast, i32 addrspace(1)** %j, align 8
91+
ret void
92+
}
93+
94+
define void @cmpxchg_captures() #0 {
95+
; CHECK-LABEL: @cmpxchg_captures(
96+
; CHECK-NEXT: entry:
97+
; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca i8*
98+
; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
99+
; CHECK-NEXT: call void @llvm.stackprotector(i8* [[STACKGUARD]], i8** [[STACKGUARDSLOT]])
100+
; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
101+
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
102+
; CHECK-NEXT: [[J:%.*]] = alloca i32*, align 8
103+
; CHECK-NEXT: store i32 0, i32* [[RETVAL]]
104+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[A]], align 4
105+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LOAD]], 1
106+
; CHECK-NEXT: store i32 [[ADD]], i32* [[A]], align 4
107+
; CHECK-NEXT: [[TMP0:%.*]] = cmpxchg i32** [[J]], i32* null, i32* [[A]] seq_cst monotonic
108+
; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
109+
; CHECK-NEXT: [[TMP1:%.*]] = load volatile i8*, i8** [[STACKGUARDSLOT]]
110+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8* [[STACKGUARD1]], [[TMP1]]
111+
; CHECK-NEXT: br i1 [[TMP2]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0
112+
; CHECK: SP_return:
113+
; CHECK-NEXT: ret void
114+
; CHECK: CallStackCheckFailBlk:
115+
; CHECK-NEXT: call void @__stack_chk_fail()
116+
; CHECK-NEXT: unreachable
117+
;
118+
entry:
119+
%retval = alloca i32, align 4
120+
%a = alloca i32, align 4
121+
%j = alloca i32*, align 8
122+
store i32 0, i32* %retval
123+
%load = load i32, i32* %a, align 4
124+
%add = add nsw i32 %load, 1
125+
store i32 %add, i32* %a, align 4
126+
127+
cmpxchg i32** %j, i32* null, i32* %a seq_cst monotonic
128+
ret void
129+
}
130+
131+
define void @memset_captures(i64 %c) #0 {
132+
; CHECK-LABEL: @memset_captures(
133+
; CHECK-NEXT: entry:
134+
; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca i8*
135+
; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
136+
; CHECK-NEXT: call void @llvm.stackprotector(i8* [[STACKGUARD]], i8** [[STACKGUARDSLOT]])
137+
; CHECK-NEXT: [[CADDR:%.*]] = alloca i64, align 8
138+
; CHECK-NEXT: store i64 %c, i64* [[CADDR]], align 8
139+
; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
140+
; CHECK-NEXT: [[IPTR:%.*]] = bitcast i32* [[I]] to i8*
141+
; CHECK-NEXT: [[COUNT:%.*]] = load i64, i64* [[CADDR]], align 8
142+
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 4 [[IPTR]], i8 0, i64 [[COUNT]], i1 false)
143+
; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
144+
; CHECK-NEXT: [[TMP1:%.*]] = load volatile i8*, i8** [[STACKGUARDSLOT]]
145+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8* [[STACKGUARD1]], [[TMP1]]
146+
; CHECK-NEXT: br i1 [[TMP2]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0
147+
; CHECK: SP_return:
148+
; CHECK-NEXT: ret void
149+
; CHECK: CallStackCheckFailBlk:
150+
; CHECK-NEXT: call void @__stack_chk_fail()
151+
; CHECK-NEXT: unreachable
152+
;
153+
entry:
154+
%c.addr = alloca i64, align 8
155+
store i64 %c, i64* %c.addr, align 8
156+
%i = alloca i32, align 4
157+
%i.ptr = bitcast i32* %i to i8*
158+
%count = load i64, i64* %c.addr, align 8
159+
call void @llvm.memset.p0i8.i64(i8* align 4 %i.ptr, i8 0, i64 %count, i1 false)
160+
ret void
161+
}
162+
163+
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg)
164+
165+
attributes #0 = { sspstrong }

0 commit comments

Comments
 (0)