Skip to content

Commit a0e1524

Browse files
committed
StackProtection: ignore pointers with no stores
Stack protection only protects against overflows, but not against out of bounds reads. rdar://105231457
1 parent 87d7e72 commit a0e1524

File tree

3 files changed

+194
-18
lines changed

3 files changed

+194
-18
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,13 +486,23 @@ private extension Instruction {
486486
if !atp.needsStackProtection {
487487
return nil
488488
}
489+
var hasNoStores = NoStores()
490+
if hasNoStores.walkDownUses(ofValue: atp, path: SmallProjectionPath()) == .continueWalk {
491+
return nil
492+
}
493+
489494
// The result of an `address_to_pointer` may be used in any unsafe way, e.g.
490495
// passed to a C function.
491496
baseAddr = atp.operand
492497
case let ia as IndexAddrInst:
493498
if !ia.needsStackProtection {
494499
return nil
495500
}
501+
var hasNoStores = NoStores()
502+
if hasNoStores.walkDownUses(ofAddress: ia, path: SmallProjectionPath()) == .continueWalk {
503+
return nil
504+
}
505+
496506
// `index_addr` is unsafe if not used for tail-allocated elements (e.g. in Array).
497507
baseAddr = ia.base
498508
default:
@@ -509,6 +519,29 @@ private extension Instruction {
509519
}
510520
}
511521

522+
/// Checks if there are no stores to an address or raw pointer.
523+
private struct NoStores : ValueDefUseWalker, AddressDefUseWalker {
524+
var walkDownCache = WalkerCache<SmallProjectionPath>()
525+
526+
mutating func leafUse(value: Operand, path: SmallProjectionPath) -> WalkResult {
527+
if let ptai = value.instruction as? PointerToAddressInst {
528+
return walkDownUses(ofAddress: ptai, path: path)
529+
}
530+
return .abortWalk
531+
}
532+
533+
mutating func leafUse(address: Operand, path: SmallProjectionPath) -> WalkResult {
534+
switch address.instruction {
535+
case is LoadInst:
536+
return .continueWalk
537+
case let cai as CopyAddrInst:
538+
return address == cai.sourceOperand ? .continueWalk : .abortWalk
539+
default:
540+
return .abortWalk
541+
}
542+
}
543+
}
544+
512545
private extension Function {
513546
func setNeedsStackProtection(_ context: FunctionPassContext) {
514547
if !needsStackProtection {

test/SILOptimizer/stack_protection.sil

Lines changed: 143 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,126 @@ struct S {
2020
@_hasStorage var b: Int64
2121
}
2222

23+
sil @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
24+
sil @unknown_addr : $@convention(thin) (@in Int64) -> ()
25+
2326
// CHECK-LABEL: sil [stack_protection] @function_local_stack
2427
// CHECK-NOT: copy_addr
2528
// CHECK: } // end sil function 'function_local_stack'
26-
sil @function_local_stack : $@convention(thin) () -> () {
29+
sil @function_local_stack : $@convention(thin) (Int64) -> () {
30+
bb0(%0 : $Int64):
31+
%1 = alloc_stack $Int64
32+
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
33+
%3 = pointer_to_address %2 : $Builtin.RawPointer to $*Int64
34+
store %0 to %3 : $*Int64
35+
dealloc_stack %1 : $*Int64
36+
%r = tuple ()
37+
return %r : $()
38+
}
39+
40+
// CHECK-LABEL: sil @only_loads
41+
// CHECK-NOT: copy_addr
42+
// CHECK: } // end sil function 'only_loads'
43+
sil @only_loads : $@convention(thin) () -> Int64 {
44+
bb0:
45+
%1 = alloc_stack $Int64
46+
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
47+
%3 = pointer_to_address %2 : $Builtin.RawPointer to $*Int64
48+
%4 = load %3 : $*Int64
49+
dealloc_stack %1 : $*Int64
50+
return %4 : $Int64
51+
}
52+
53+
// CHECK-LABEL: sil @copy_addr_src
54+
// CHECK: } // end sil function 'copy_addr_src'
55+
sil @copy_addr_src : $@convention(thin) () -> @out Int64 {
56+
bb0(%0 : $*Int64):
57+
%1 = alloc_stack $Int64
58+
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
59+
%3 = pointer_to_address %2 : $Builtin.RawPointer to $*Int64
60+
copy_addr %3 to %0 : $*Int64
61+
dealloc_stack %1 : $*Int64
62+
%r = tuple ()
63+
return %r : $()
64+
}
65+
66+
// CHECK-LABEL: sil [stack_protection] @copy_addr_dst
67+
// CHECK: } // end sil function 'copy_addr_dst'
68+
sil @copy_addr_dst : $@convention(thin) (@in Int64) -> () {
69+
bb0(%0 : $*Int64):
70+
%1 = alloc_stack $Int64
71+
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
72+
%3 = pointer_to_address %2 : $Builtin.RawPointer to $*Int64
73+
copy_addr %0 to %3 : $*Int64
74+
dealloc_stack %1 : $*Int64
75+
%r = tuple ()
76+
return %r : $()
77+
}
78+
79+
// CHECK-LABEL: sil [stack_protection] @escaping_pointer
80+
// CHECK-NOT: copy_addr
81+
// CHECK: } // end sil function 'escaping_pointer'
82+
sil @escaping_pointer : $@convention(thin) () -> () {
83+
bb0:
84+
%1 = alloc_stack $Int64
85+
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
86+
%3 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
87+
apply %3(%2) : $@convention(thin) (Builtin.RawPointer) -> ()
88+
dealloc_stack %1 : $*Int64
89+
%r = tuple ()
90+
return %r : $()
91+
}
92+
93+
// CHECK-LABEL: sil @no_stack_protection_for_index_addr
94+
// CHECK-NOT: copy_addr
95+
// CHECK: } // end sil function 'no_stack_protection_for_index_addr'
96+
sil @no_stack_protection_for_index_addr : $@convention(thin) () -> () {
2797
bb0:
2898
%0 = alloc_stack $Int64
29-
%1 = address_to_pointer [stack_protection] %0 : $*Int64 to $Builtin.RawPointer
99+
%1 = address_to_pointer %0 : $*Int64 to $Builtin.RawPointer
100+
%2 = integer_literal $Builtin.Word, 1
101+
%3 = index_addr %0 : $*Int64, %2 : $Builtin.Word
102+
%4 = function_ref @unknown_addr : $@convention(thin) (@in Int64) -> ()
103+
apply %4(%3) : $@convention(thin) (@in Int64) -> ()
30104
dealloc_stack %0 : $*Int64
31105
%r = tuple ()
32106
return %r : $()
33107
}
34108

35-
// CHECK-LABEL: sil @no_stack_protection
109+
// CHECK-LABEL: sil [stack_protection] @stack_protection_for_index_addr
36110
// CHECK-NOT: copy_addr
37-
// CHECK: } // end sil function 'no_stack_protection'
38-
sil @no_stack_protection : $@convention(thin) () -> () {
111+
// CHECK: } // end sil function 'stack_protection_for_index_addr'
112+
sil @stack_protection_for_index_addr : $@convention(thin) () -> () {
39113
bb0:
40114
%0 = alloc_stack $Int64
41115
%1 = address_to_pointer %0 : $*Int64 to $Builtin.RawPointer
42116
%2 = integer_literal $Builtin.Word, 1
43-
%3 = index_addr %0 : $*Int64, %2 : $Builtin.Word
117+
%3 = index_addr [stack_protection] %0 : $*Int64, %2 : $Builtin.Word
118+
%4 = function_ref @unknown_addr : $@convention(thin) (@in Int64) -> ()
119+
apply %4(%3) : $@convention(thin) (@in Int64) -> ()
44120
dealloc_stack %0 : $*Int64
45121
%r = tuple ()
46122
return %r : $()
47123
}
48124

49-
// CHECK-LABEL: sil [stack_protection] @stack_alloc_builtin
125+
// CHECK-LABEL: sil @index_addr_with_only_loads
50126
// CHECK-NOT: copy_addr
51-
// CHECK: } // end sil function 'stack_alloc_builtin'
52-
sil @stack_alloc_builtin : $@convention(thin) () -> () {
127+
// CHECK: } // end sil function 'index_addr_with_only_loads'
128+
sil @index_addr_with_only_loads : $@convention(thin) () -> Int64 {
129+
bb0:
130+
%0 = alloc_stack $Int64
131+
%1 = address_to_pointer %0 : $*Int64 to $Builtin.RawPointer
132+
%2 = integer_literal $Builtin.Word, 1
133+
%3 = index_addr [stack_protection] %0 : $*Int64, %2 : $Builtin.Word
134+
%4 = load %3 : $*Int64
135+
dealloc_stack %0 : $*Int64
136+
return %4 : $Int64
137+
}
138+
139+
// CHECK-LABEL: sil [stack_protection] @stack_alloc_builtin_with_3_args
140+
// CHECK-NOT: copy_addr
141+
// CHECK: } // end sil function 'stack_alloc_builtin_with_3_args'
142+
sil @stack_alloc_builtin_with_3_args : $@convention(thin) () -> () {
53143
bb0:
54144
%0 = integer_literal $Builtin.Word, 8
55145
%1 = builtin "stackAlloc"(%0 : $Builtin.Word, %0 : $Builtin.Word, %0 : $Builtin.Word) : $Builtin.RawPointer
@@ -66,6 +156,8 @@ bb0:
66156
%1 = ref_element_addr %0 : $C, #C.i
67157
%2 = begin_access [modify] [static] %1 : $*Int64
68158
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
159+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
160+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
69161
end_access %2 : $*Int64
70162
dealloc_stack_ref %0 : $C
71163
%r = tuple ()
@@ -81,6 +173,8 @@ bb0:
81173
%1 = ref_element_addr %0 : $C, #C.i
82174
%2 = begin_access [modify] [static] %1 : $*Int64
83175
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
176+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
177+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
84178
end_access %2 : $*Int64
85179
strong_release %0 : $C
86180
%r = tuple ()
@@ -101,6 +195,8 @@ bb0:
101195
sil @inout_with_unknown_callers1 : $@convention(thin) (@inout Int64) -> () {
102196
bb0(%0 : $*Int64):
103197
%1 = address_to_pointer [stack_protection] %0 : $*Int64 to $Builtin.RawPointer
198+
%2 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
199+
apply %2(%1) : $@convention(thin) (Builtin.RawPointer) -> ()
104200
%r = tuple ()
105201
return %r : $()
106202
}
@@ -112,6 +208,8 @@ bb0(%0 : $*Int64):
112208
sil @in_with_unknown_callers : $@convention(thin) (@in_guaranteed Int64) -> () {
113209
bb0(%0 : $*Int64):
114210
%1 = address_to_pointer [stack_protection] %0 : $*Int64 to $Builtin.RawPointer
211+
%2 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
212+
apply %2(%1) : $@convention(thin) (Builtin.RawPointer) -> ()
115213
%r = tuple ()
116214
return %r : $()
117215
}
@@ -132,6 +230,8 @@ sil @inout_with_modify_access : $@convention(thin) (@inout Int64) -> () {
132230
bb0(%0 : $*Int64):
133231
%1 = begin_access [modify] [dynamic] %0 : $*Int64
134232
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
233+
%3 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
234+
apply %3(%2) : $@convention(thin) (Builtin.RawPointer) -> ()
135235
end_access %1 : $*Int64
136236
%r = tuple ()
137237
return %r : $()
@@ -146,6 +246,8 @@ sil @inout_with_read_access : $@convention(thin) (@inout Int64) -> () {
146246
bb0(%0 : $*Int64):
147247
%1 = begin_access [read] [dynamic] %0 : $*Int64
148248
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
249+
%3 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
250+
apply %3(%2) : $@convention(thin) (Builtin.RawPointer) -> ()
149251
end_access %1 : $*Int64
150252
%r = tuple ()
151253
return %r : $()
@@ -168,8 +270,11 @@ sil @inout_with_unknown_callers2 : $@convention(thin) (@inout S) -> () {
168270
bb0(%0 : $*S):
169271
%1 = struct_element_addr %0 : $*S, #S.a
170272
%2 = address_to_pointer [stack_protection] %1 : $*Int64 to $Builtin.RawPointer
171-
%3 = struct_element_addr %0 : $*S, #S.b
172-
%4 = address_to_pointer [stack_protection] %3 : $*Int64 to $Builtin.RawPointer
273+
%3 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
274+
apply %3(%2) : $@convention(thin) (Builtin.RawPointer) -> ()
275+
%5 = struct_element_addr %0 : $*S, #S.b
276+
%6 = address_to_pointer [stack_protection] %5 : $*Int64 to $Builtin.RawPointer
277+
apply %3(%6) : $@convention(thin) (Builtin.RawPointer) -> ()
173278
%r = tuple ()
174279
return %r : $()
175280
}
@@ -192,6 +297,8 @@ bb0(%0 : $C):
192297
%1 = ref_element_addr %0 : $C, #C.i
193298
%2 = begin_access [modify] [static] %1 : $*Int64
194299
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
300+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
301+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
195302
end_access %2 : $*Int64
196303
%r = tuple ()
197304
return %r : $()
@@ -216,7 +323,10 @@ bb0(%0 : $C):
216323
%1 = ref_element_addr %0 : $C, #C.i
217324
%2 = begin_access [modify] [static] %1 : $*Int64
218325
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
219-
%4 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
326+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
327+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
328+
%6 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
329+
apply %4(%6) : $@convention(thin) (Builtin.RawPointer) -> ()
220330
end_access %2 : $*Int64
221331
%r = tuple ()
222332
return %r : $()
@@ -249,11 +359,14 @@ bb0(%0 : $C):
249359
// Accesses don't need to be properly nested.
250360
%2 = begin_access [modify] [static] %1 : $*Int64
251361
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
252-
%4 = ref_element_addr %0 : $C, #C.j
253-
%5 = begin_access [modify] [static] %4 : $*Int64
362+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
363+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
364+
%6 = ref_element_addr %0 : $C, #C.j
365+
%7 = begin_access [modify] [static] %6 : $*Int64
254366
end_access %2 : $*Int64
255-
%7 = address_to_pointer [stack_protection] %5 : $*Int64 to $Builtin.RawPointer
256-
end_access %5 : $*Int64
367+
%9 = address_to_pointer [stack_protection] %7 : $*Int64 to $Builtin.RawPointer
368+
apply %4(%9) : $@convention(thin) (Builtin.RawPointer) -> ()
369+
end_access %7 : $*Int64
257370
%r = tuple ()
258371
return %r : $()
259372
}
@@ -270,6 +383,8 @@ bb0(%0 : $C):
270383
%1 = ref_element_addr %0 : $C, #C.i
271384
%2 = begin_access [read] [static] %1 : $*Int64
272385
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
386+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
387+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
273388
end_access %2 : $*Int64
274389
%r = tuple ()
275390
return %r : $()
@@ -368,8 +483,10 @@ bb0(%0 : $*Int64):
368483
sil @partially_known_callers : $@convention(thin) (@inout Int64) -> () {
369484
bb0(%0 : $*Int64):
370485
%1 = address_to_pointer [stack_protection] %0 : $*Int64 to $Builtin.RawPointer
371-
%2 = tuple ()
372-
return %2 : $()
486+
%2 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
487+
apply %2(%1) : $@convention(thin) (Builtin.RawPointer) -> ()
488+
%4 = tuple ()
489+
return %4 : $()
373490
}
374491

375492
// MOVE-LABEL: sil @call_partially_known_callers
@@ -394,6 +511,8 @@ bb0(%0 : $C):
394511
%1 = ref_element_addr %0 : $C, #C.i
395512
%2 = begin_access [modify] [static] %1 : $*Int64
396513
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
514+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
515+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
397516
end_access %2 : $*Int64
398517
%7 = tuple ()
399518
return %7 : $()
@@ -445,6 +564,8 @@ bb0(%0 : $C):
445564
%1 = ref_element_addr %0 : $C, #C.i
446565
%2 = begin_access [modify] [static] %1 : $*Int64
447566
%3 = address_to_pointer [stack_protection] %2 : $*Int64 to $Builtin.RawPointer
567+
%4 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
568+
apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> ()
448569
end_access %2 : $*Int64
449570
%7 = tuple ()
450571
return %7 : $()
@@ -479,6 +600,8 @@ bb3(%5 : $C):
479600
sil private @closure_with_inout_capture : $@convention(thin) (@inout_aliasable Int64) -> () {
480601
bb0(%0 : $*Int64):
481602
%1 = address_to_pointer [stack_protection] %0 : $*Int64 to $Builtin.RawPointer
603+
%2 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
604+
apply %2(%1) : $@convention(thin) (Builtin.RawPointer) -> ()
482605
%r = tuple ()
483606
return %r : $()
484607
}
@@ -517,6 +640,8 @@ bb0:
517640
sil private @closure_with_inout_arg : $@convention(thin) (@inout Int64) -> () {
518641
bb0(%0 : $*Int64):
519642
%1 = address_to_pointer [stack_protection] %0 : $*Int64 to $Builtin.RawPointer
643+
%2 = function_ref @unknown : $@convention(thin) (Builtin.RawPointer) -> ()
644+
apply %2(%1) : $@convention(thin) (Builtin.RawPointer) -> ()
520645
%r = tuple ()
521646
return %r : $()
522647
}

test/SILOptimizer/stack_protection.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,24 @@ public func overflowWithUnsafeBytes() {
3636
}
3737
}
3838

39+
// CHECK-LABEL: sil [stack_protection] @$s4test31owerflowWithUnsafeBorrowedBytes5valueySi_tF
40+
// CHECK-NOT: copy_addr
41+
// CHECK: } // end sil function '$s4test31owerflowWithUnsafeBorrowedBytes5valueySi_tF'
42+
public func owerflowWithUnsafeBorrowedBytes(value: Int) {
43+
withUnsafeBytes(of: value) {
44+
potentiallyBadCFunction($0.bindMemory(to: Int.self).baseAddress!)
45+
}
46+
}
47+
48+
// CHECK-LABEL: sil @$s4test9onlyLoads5valueS2i_tF
49+
// CHECK-NOT: copy_addr
50+
// CHECK: } // end sil function '$s4test9onlyLoads5valueS2i_tF'
51+
public func onlyLoads(value: Int) -> Int {
52+
withUnsafeBytes(of: value) {
53+
$0.load(as: Int.self)
54+
}
55+
}
56+
3957
// CHECK-LABEL: sil @$s4test22unprotectedUnsafeBytesyyF
4058
// CHECK-NOT: copy_addr
4159
// CHECK: } // end sil function '$s4test22unprotectedUnsafeBytesyyF'

0 commit comments

Comments
 (0)