Skip to content

Commit b8d8949

Browse files
lorenteymilseman
authored andcommitted
String & String views: Add bounds checking to range subscripts.
1 parent 6d1866f commit b8d8949

File tree

4 files changed

+58
-0
lines changed

4 files changed

+58
-0
lines changed

stdlib/public/core/StringRangeReplaceableCollection.swift.gyb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,34 @@ extension String : StringProtocol, RangeReplaceableCollection {
8989
@_inlineable // FIXME(sil-serialize-all)
9090
public var endIndex: Index { return Index(encodedOffset: _guts.count) }
9191

92+
@_inlineable
93+
@_versioned
94+
@inline(__always)
95+
internal func _boundsCheck(_ index: Index) {
96+
_precondition(index.encodedOffset >= 0 && index.encodedOffset < _guts.count,
97+
"String index is out of bounds")
98+
}
99+
100+
@_inlineable
101+
@_versioned
102+
@inline(__always)
103+
internal func _boundsCheck(_ range: Range<Index>) {
104+
_precondition(
105+
range.lowerBound.encodedOffset >= 0 &&
106+
range.upperBound.encodedOffset <= _guts.count,
107+
"String index range is out of bounds")
108+
}
109+
110+
@_inlineable
111+
@_versioned
112+
@inline(__always)
113+
internal func _boundsCheck(_ range: ClosedRange<Index>) {
114+
_precondition(
115+
range.lowerBound.encodedOffset >= 0 &&
116+
range.upperBound.encodedOffset < _guts.count,
117+
"String index range is out of bounds")
118+
}
119+
92120
@_inlineable // FIXME(sil-serialize-all)
93121
@_versioned // FIXME(sil-serialize-all)
94122
internal func _index(atEncodedOffset offset: Int) -> Index {

stdlib/public/core/StringStorage.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import SwiftShims
1414

15+
@_fixed_layout
1516
public
1617
class _SwiftRawStringStorage : _SwiftNativeNSString {
1718
@nonobjc
@@ -46,6 +47,7 @@ class _SwiftRawStringStorage : _SwiftNativeNSString {
4647
internal typealias _ASCIIStringStorage = _SwiftStringStorage<UInt8>
4748
internal typealias _UTF16StringStorage = _SwiftStringStorage<UTF16.CodeUnit>
4849

50+
@_fixed_layout
4951
public final class _SwiftStringStorage<CodeUnit>
5052
: _SwiftRawStringStorage, _NSStringCore
5153
where CodeUnit : UnsignedInteger & FixedWidthInteger {

stdlib/public/core/Substring.swift.gyb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,9 @@ extension Substring.${View} : BidirectionalCollection {
433433
}
434434
@_inlineable // FIXME(sil-serialize-all)
435435
public subscript(r: Range<Index>) -> SubSequence {
436+
// FIXME(strings): tests.
437+
_precondition(r.lowerBound >= startIndex && r.upperBound <= endIndex,
438+
"${View} index range out of bounds")
436439
return Substring.${View}(_wholeString.${property}, _bounds: r)
437440
}
438441
}
@@ -607,19 +610,22 @@ extension String {
607610
@_inlineable // FIXME(sil-serialize-all)
608611
@available(swift, introduced: 4)
609612
public subscript(r: Range<Index>) -> Substring {
613+
_boundsCheck(r)
610614
return Substring(
611615
_slice: Slice(base: self, bounds: r))
612616
}
613617

614618
@_inlineable // FIXME(sil-serialize-all)
615619
@available(swift, obsoleted: 4)
616620
public subscript(bounds: Range<Index>) -> String {
621+
_boundsCheck(bounds)
617622
return String(Substring(_slice: Slice(base: self, bounds: bounds)))
618623
}
619624

620625
@_inlineable // FIXME(sil-serialize-all)
621626
@available(swift, obsoleted: 4)
622627
public subscript(bounds: ClosedRange<Index>) -> String {
628+
_boundsCheck(bounds)
623629
return String(Substring(_slice: Slice(
624630
base: self,
625631
bounds: bounds.relative(to: self))))

validation-test/stdlib/String.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,28 @@ StringTests.test("ForeignIndexes/subscript(Range)/OutOfBoundsTrap/2") {
301301
_ = acceptor[r]
302302
}
303303

304+
StringTests.test("ForeignIndexes/subscript(ClosedRange)/OutOfBoundsTrap/1") {
305+
let donor = "abcdef"
306+
let acceptor = "uvw"
307+
308+
expectEqual("uvw", acceptor[donor.startIndex...donor.index(_nth: 2)])
309+
310+
let r = donor.startIndex...donor.index(_nth: 3)
311+
expectCrashLater()
312+
_ = acceptor[r]
313+
}
314+
315+
StringTests.test("ForeignIndexes/subscript(ClosedRange)/OutOfBoundsTrap/2") {
316+
let donor = "abcdef"
317+
let acceptor = "uvw"
318+
319+
expectEqual("uvw", acceptor[donor.startIndex...donor.index(_nth: 2)])
320+
321+
let r = donor.index(_nth: 3)...donor.index(_nth: 5)
322+
expectCrashLater()
323+
_ = acceptor[r]
324+
}
325+
304326
StringTests.test("ForeignIndexes/replaceSubrange/OutOfBoundsTrap/1") {
305327
let donor = "abcdef"
306328
var acceptor = "uvw"

0 commit comments

Comments
 (0)