Skip to content

Commit d31ffc7

Browse files
committed
[Foundation] Validate indexes and ranges passed into Data so that bounding conditions are respected
1 parent c98ea8b commit d31ffc7

File tree

2 files changed

+44
-25
lines changed

2 files changed

+44
-25
lines changed

stdlib/public/SDK/Foundation/Data.swift

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ public final class _DataStorage {
319319

320320
@inline(__always)
321321
public func append(_ bytes: UnsafeRawPointer, length: Int) {
322+
precondition(length >= 0, "Length of appending bytes must be positive")
322323
switch _backing {
323324
case .swift:
324325
let origLength = _length
@@ -1087,6 +1088,18 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
10871088
_sliceRange = range
10881089
}
10891090

1091+
@_versioned
1092+
internal func _validateIndex(_ index: Int, message: String? = nil) {
1093+
precondition(_sliceRange.contains(index), message ?? "Index \(index) is out of bounds of range \(_sliceRange)")
1094+
}
1095+
1096+
@_versioned
1097+
internal func _validateRange<R: RangeExpression>(_ range: R) where R.Bound == Int {
1098+
let r = range.relative(to: 0..<R.Bound.max)
1099+
precondition(r.lowerBound >= _sliceRange.lowerBound && r.lowerBound <= _sliceRange.upperBound, "Range \(r) is out of bounds of range \(_sliceRange)")
1100+
precondition(r.upperBound >= _sliceRange.lowerBound && r.upperBound <= _sliceRange.upperBound, "Range \(r) is out of bounds of range \(_sliceRange)")
1101+
}
1102+
10901103
// -----------------------------------
10911104
// MARK: - Properties and Functions
10921105

@@ -1099,6 +1112,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
10991112
}
11001113
@inline(__always)
11011114
set {
1115+
precondition(count >= 0, "count must be positive")
11021116
if !isKnownUniquelyReferenced(&_backing) {
11031117
_backing = _backing.mutableCopy(_sliceRange)
11041118
}
@@ -1142,14 +1156,15 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
11421156
/// - warning: This method does not verify that the contents at pointer have enough space to hold `count` bytes.
11431157
@inline(__always)
11441158
public func copyBytes(to pointer: UnsafeMutablePointer<UInt8>, count: Int) {
1159+
precondition(count >= 0, "count of bytes to copy must be positive")
11451160
if count == 0 { return }
1146-
memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: _sliceRange.lowerBound), count)
1161+
memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: _sliceRange.lowerBound), Swift.min(count, _sliceRange.count))
11471162
}
11481163

11491164
@inline(__always)
11501165
private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: NSRange) {
11511166
if range.length == 0 { return }
1152-
memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: range.location), range.length)
1167+
memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: range.location), Swift.min(range.length, _sliceRange.count))
11531168
}
11541169

11551170
/// Copy a subset of the contents of the data to a pointer.
@@ -1174,18 +1189,13 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
11741189

11751190
let copyRange : Range<Index>
11761191
if let r = range {
1177-
guard !r.isEmpty else { return 0 }
1178-
precondition(r.lowerBound >= 0)
1179-
precondition(r.lowerBound < cnt, "The range is outside the bounds of the data")
1180-
1181-
precondition(r.upperBound >= 0)
1182-
precondition(r.upperBound <= cnt, "The range is outside the bounds of the data")
1183-
1192+
guard !r.isEmpty else { return 0 }
11841193
copyRange = r.lowerBound..<(r.lowerBound + Swift.min(buffer.count * MemoryLayout<DestinationType>.stride, r.count))
11851194
} else {
11861195
copyRange = 0..<Swift.min(buffer.count * MemoryLayout<DestinationType>.stride, cnt)
11871196
}
1188-
1197+
_validateRange(copyRange)
1198+
11891199
guard !copyRange.isEmpty else { return 0 }
11901200

11911201
let nsRange = NSMakeRange(copyRange.lowerBound, copyRange.upperBound - copyRange.lowerBound)
@@ -1243,6 +1253,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
12431253
public func range(of dataToFind: Data, options: Data.SearchOptions = [], in range: Range<Index>? = nil) -> Range<Index>? {
12441254
let nsRange : NSRange
12451255
if let r = range {
1256+
_validateRange(r)
12461257
nsRange = NSMakeRange(r.lowerBound, r.upperBound - r.lowerBound)
12471258
} else {
12481259
nsRange = NSMakeRange(0, _backing.length)
@@ -1266,6 +1277,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
12661277

12671278
@inline(__always)
12681279
public mutating func append(_ bytes: UnsafePointer<UInt8>, count: Int) {
1280+
precondition(count >= 0, "count must be positive")
12691281
if count == 0 { return }
12701282
if !isKnownUniquelyReferenced(&_backing) {
12711283
_backing = _backing.mutableCopy(_sliceRange)
@@ -1326,6 +1338,9 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
13261338
/// - parameter range: The range in the data to set to `0`.
13271339
@inline(__always)
13281340
public mutating func resetBytes(in range: Range<Index>) {
1341+
// it is worth noting that the range here may be out of bounds of the Data itself (which triggers a growth)
1342+
precondition(range.lowerBound >= 0, "Ranges must be positive bounds")
1343+
precondition(range.upperBound >= 0, "Ranges must be positive bounds")
13291344
let range = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound)
13301345
if !isKnownUniquelyReferenced(&_backing) {
13311346
_backing = _backing.mutableCopy(_sliceRange)
@@ -1346,6 +1361,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
13461361
/// - parameter data: The replacement data.
13471362
@inline(__always)
13481363
public mutating func replaceSubrange(_ subrange: Range<Index>, with data: Data) {
1364+
_validateRange(subrange)
13491365
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
13501366
let cnt = data.count
13511367
if !isKnownUniquelyReferenced(&_backing) {
@@ -1361,6 +1377,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
13611377

13621378
@inline(__always)
13631379
public mutating func replaceSubrange(_ subrange: CountableRange<Index>, with data: Data) {
1380+
_validateRange(subrange)
13641381
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
13651382
let cnt = data.count
13661383
if !isKnownUniquelyReferenced(&_backing) {
@@ -1383,6 +1400,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
13831400
/// - parameter buffer: The replacement bytes.
13841401
@inline(__always)
13851402
public mutating func replaceSubrange<SourceType>(_ subrange: Range<Index>, with buffer: UnsafeBufferPointer<SourceType>) {
1403+
_validateRange(subrange)
13861404
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
13871405
let bufferCount = buffer.count * MemoryLayout<SourceType>.stride
13881406

@@ -1405,19 +1423,13 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
14051423
@inline(__always)
14061424
public mutating func replaceSubrange<ByteCollection : Collection>(_ subrange: Range<Index>, with newElements: ByteCollection)
14071425
where ByteCollection.Iterator.Element == Data.Iterator.Element {
1408-
1426+
_validateRange(subrange)
14091427
// Calculate this once, it may not be O(1)
14101428
let replacementCount: Int = numericCast(newElements.count)
14111429
let currentCount = self.count
14121430
let subrangeCount = subrange.count
14131431

1414-
if currentCount < subrange.lowerBound + subrangeCount {
1415-
if subrangeCount == 0 {
1416-
preconditionFailure("location \(subrange.lowerBound) exceeds data count \(currentCount)")
1417-
} else {
1418-
preconditionFailure("range \(subrange) exceeds data count \(currentCount)")
1419-
}
1420-
}
1432+
_validateRange(subrange)
14211433

14221434
let resultCount = currentCount - subrangeCount + replacementCount
14231435
if resultCount != currentCount {
@@ -1446,6 +1458,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
14461458

14471459
@inline(__always)
14481460
public mutating func replaceSubrange(_ subrange: Range<Index>, with bytes: UnsafeRawPointer, count cnt: Int) {
1461+
_validateRange(subrange)
14491462
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
14501463
if !isKnownUniquelyReferenced(&_backing) {
14511464
_backing = _backing.mutableCopy(_sliceRange)
@@ -1461,11 +1474,11 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
14611474
/// - parameter range: The range to copy.
14621475
@inline(__always)
14631476
public func subdata(in range: Range<Index>) -> Data {
1477+
_validateRange(range)
14641478
let length = count
14651479
if count == 0 {
14661480
return Data()
14671481
}
1468-
precondition(length >= range.upperBound)
14691482
return _backing.subdata(in: range)
14701483
}
14711484

@@ -1502,6 +1515,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
15021515

15031516
@inline(__always)
15041517
public func advanced(by amount: Int) -> Data {
1518+
_validateIndex(startIndex + amount)
15051519
let length = count - amount
15061520
precondition(length > 0)
15071521
return withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> Data in
@@ -1518,10 +1532,12 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
15181532
public subscript(index: Index) -> UInt8 {
15191533
@inline(__always)
15201534
get {
1535+
_validateIndex(index)
15211536
return _backing.bytes!.advanced(by: index).assumingMemoryBound(to: UInt8.self).pointee
15221537
}
15231538
@inline(__always)
15241539
set {
1540+
_validateIndex(index)
15251541
if !isKnownUniquelyReferenced(&_backing) {
15261542
_backing = _backing.mutableCopy(_sliceRange)
15271543
}
@@ -1532,6 +1548,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
15321548
public subscript(bounds: Range<Index>) -> Data {
15331549
@inline(__always)
15341550
get {
1551+
_validateRange(bounds)
15351552
return Data(backing: _backing, range: bounds)
15361553
}
15371554
@inline(__always)
@@ -1547,16 +1564,20 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
15471564
let range = rangeExpression.relative(to: 0..<R.Bound.max)
15481565
let start: Int = numericCast(range.lowerBound)
15491566
let end: Int = numericCast(range.upperBound)
1550-
return Data(backing: _backing, range: start..<end)
1567+
let r: Range<Int> = start..<end
1568+
_validateRange(r)
1569+
return Data(backing: _backing, range: r)
15511570
}
15521571
@inline(__always)
15531572
set {
15541573
let range = rangeExpression.relative(to: 0..<R.Bound.max)
15551574
let start: Int = numericCast(range.lowerBound)
15561575
let end: Int = numericCast(range.upperBound)
1557-
replaceSubrange(start..<end, with: newValue)
1576+
let r: Range<Int> = start..<end
1577+
_validateRange(r)
1578+
replaceSubrange(r, with: newValue)
15581579
}
1559-
1580+
15601581
}
15611582

15621583
/// The start `Index` in the data.

test/stdlib/TestData.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,9 +1236,7 @@ DataTests.test("bounding failure append absurd length") {
12361236
data.append("hello", count: Int.min)
12371237
}
12381238

1239-
DataTests.test("bounding failure subscript")
1240-
.skip(.always("fails with resilient stdlib (rdar://problem/30560514)"))
1241-
.code {
1239+
DataTests.test("bounding failure subscript") {
12421240
var data = "Hello World".data(using: .utf8)!
12431241
expectCrashLater()
12441242
data[100] = 4

0 commit comments

Comments
 (0)