Skip to content

Commit cf1b9d9

Browse files
committed
[stdlib] String: Fix more potential UB, and rework access patterns
1 parent 86467bb commit cf1b9d9

File tree

4 files changed

+67
-38
lines changed

4 files changed

+67
-38
lines changed

stdlib/public/core/StringGuts.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ extension _StringGuts {
123123
// Whether this string has breadcrumbs
124124
internal var hasBreadcrumbs: Bool {
125125
return hasSharedStorage
126-
|| (hasNativeStorage && _object.nativeStorage.hasBreadcrumbs)
126+
|| (hasNativeStorage && _object.withNativeStorage { $0.hasBreadcrumbs })
127127
}
128128
}
129129

stdlib/public/core/StringGutsRangeReplaceable.swift

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@
1313
// COW helpers
1414
extension _StringGuts {
1515
internal var nativeCapacity: Int? {
16-
guard hasNativeStorage else { return nil }
17-
return _object.nativeStorage.capacity
16+
guard hasNativeStorage else { return nil }
17+
return _object.withNativeStorage { $0.capacity }
1818
}
1919

2020
internal var nativeUnusedCapacity: Int? {
21-
guard hasNativeStorage else { return nil }
22-
return _object.nativeStorage.unusedCapacity
21+
guard hasNativeStorage else { return nil }
22+
return _object.withNativeStorage { $0.unusedCapacity }
2323
}
2424

2525
// If natively stored and uniquely referenced, return the storage's total
2626
// capacity. Otherwise, nil.
2727
internal var uniqueNativeCapacity: Int? {
2828
@inline(__always) mutating get {
2929
guard isUniqueNative else { return nil }
30-
return _object.nativeStorage.capacity
30+
return _object.withNativeStorage { $0.capacity }
3131
}
3232
}
3333

@@ -36,7 +36,7 @@ extension _StringGuts {
3636
internal var uniqueNativeUnusedCapacity: Int? {
3737
@inline(__always) mutating get {
3838
guard isUniqueNative else { return nil }
39-
return _object.nativeStorage.unusedCapacity
39+
return _object.withNativeStorage { $0.unusedCapacity }
4040
}
4141
}
4242

@@ -54,6 +54,20 @@ extension _StringGuts {
5454

5555
// Range-replaceable operation support
5656
extension _StringGuts {
57+
@inline(__always)
58+
internal mutating func updateNativeStorage<R>(
59+
_ body: (__StringStorage) -> R
60+
) -> R {
61+
let storage = self._object.nativeStorage
62+
self = _StringGuts()
63+
defer {
64+
// We re-initialize from the modified storage to pick up new count, flags,
65+
// etc.
66+
self = _StringGuts(storage)
67+
}
68+
return body(storage)
69+
}
70+
5771
@inlinable
5872
internal init(_initialCapacity capacity: Int) {
5973
self.init()
@@ -243,11 +257,7 @@ extension _StringGuts {
243257
internal mutating func appendInPlace(
244258
_ other: UnsafeBufferPointer<UInt8>, isASCII: Bool
245259
) {
246-
self._object.nativeStorage.appendInPlace(other, isASCII: isASCII)
247-
248-
// We re-initialize from the modified storage to pick up new count, flags,
249-
// etc.
250-
self = _StringGuts(self._object.nativeStorage)
260+
updateNativeStorage { $0.appendInPlace(other, isASCII: isASCII) }
251261
}
252262

253263
@inline(never) // slow-path
@@ -256,11 +266,7 @@ extension _StringGuts {
256266
_internalInvariant(self.uniqueNativeUnusedCapacity != nil)
257267

258268
var iter = Substring(other).utf8.makeIterator()
259-
self._object.nativeStorage.appendInPlace(&iter, isASCII: other.isASCII)
260-
261-
// We re-initialize from the modified storage to pick up new count, flags,
262-
// etc.
263-
self = _StringGuts(self._object.nativeStorage)
269+
updateNativeStorage { $0.appendInPlace(&iter, isASCII: other.isASCII) }
264270
}
265271

266272
internal mutating func clear() {
@@ -270,8 +276,7 @@ extension _StringGuts {
270276
}
271277

272278
// Reset the count
273-
_object.nativeStorage.clear()
274-
self = _StringGuts(_object.nativeStorage)
279+
updateNativeStorage { $0.clear() }
275280
}
276281

277282
internal mutating func remove(from lower: Index, to upper: Index) {
@@ -281,10 +286,7 @@ extension _StringGuts {
281286
_internalInvariant(lowerOffset <= upperOffset && upperOffset <= self.count)
282287

283288
if isUniqueNative {
284-
_object.nativeStorage.remove(from: lowerOffset, to: upperOffset)
285-
// We re-initialize from the modified storage to pick up new count, flags,
286-
// etc.
287-
self = _StringGuts(self._object.nativeStorage)
289+
updateNativeStorage { $0.remove(from: lowerOffset, to: upperOffset) }
288290
return
289291
}
290292

@@ -412,8 +414,9 @@ extension _StringGuts {
412414

413415
let start = bounds.lowerBound._encodedOffset
414416
let end = bounds.upperBound._encodedOffset
415-
_object.nativeStorage.replace(from: start, to: end, with: codeUnits)
416-
self = _StringGuts(_object.nativeStorage)
417+
updateNativeStorage {
418+
$0.replace(from: start, to: end, with: codeUnits)
419+
}
417420
return Range(_uncheckedBounds: (start, start + codeUnits.count))
418421
}
419422

@@ -435,9 +438,10 @@ extension _StringGuts {
435438

436439
let start = bounds.lowerBound._encodedOffset
437440
let end = bounds.upperBound._encodedOffset
438-
_object.nativeStorage.replace(
439-
from: start, to: end, with: codeUnits, replacementCount: replCount)
440-
self = _StringGuts(_object.nativeStorage)
441+
updateNativeStorage {
442+
$0.replace(
443+
from: start, to: end, with: codeUnits, replacementCount: replCount)
444+
}
441445
return Range(_uncheckedBounds: (start, start + replCount))
442446
}
443447

stdlib/public/core/StringObject.swift

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,13 @@ extension _StringObject {
894894
discriminatedObjectRawBits & Nibbles.largeAddressMask)
895895
}
896896

897+
@inline(__always)
898+
@_alwaysEmitIntoClient
899+
internal var largeAddress: UnsafeRawPointer {
900+
UnsafeRawPointer(bitPattern: largeAddressBits)
901+
._unsafelyUnwrappedUnchecked
902+
}
903+
897904
@inlinable @inline(__always)
898905
internal var nativeUTF8Start: UnsafePointer<UInt8> {
899906
_internalInvariant(largeFastIsTailAllocated)
@@ -940,10 +947,23 @@ extension _StringObject {
940947
return _unsafeUncheckedDowncast(storage, to: __StringStorage.self)
941948
#else
942949
_internalInvariant(hasNativeStorage)
943-
let storageAddress =
944-
UnsafeRawPointer(bitPattern:largeAddressBits)._unsafelyUnwrappedUnchecked
945-
let unmanagedRef = Unmanaged<__StringStorage>.fromOpaque(storageAddress)
946-
return unmanagedRef.takeUnretainedValue()
950+
let unmanaged = Unmanaged<__StringStorage>.fromOpaque(largeAddress)
951+
return unmanaged.takeUnretainedValue()
952+
#endif
953+
}
954+
955+
/// Call `body` with the native storage object of `self`, without retaining
956+
/// it.
957+
@inline(__always)
958+
internal func withNativeStorage<R>(
959+
_ body: (__StringStorage) -> R
960+
) -> R {
961+
#if arch(i386) || arch(arm) || arch(arm64_32) || arch(wasm32)
962+
return body(nativeStorage)
963+
#else
964+
_internalInvariant(hasNativeStorage)
965+
let unmanaged = Unmanaged<__StringStorage>.fromOpaque(largeAddress)
966+
return unmanaged._withUnsafeGuaranteedRef { body($0) }
947967
#endif
948968
}
949969

@@ -957,7 +977,8 @@ extension _StringObject {
957977
#else
958978
_internalInvariant(largeFastIsShared && !largeIsCocoa)
959979
_internalInvariant(hasSharedStorage)
960-
return Builtin.reinterpretCast(largeAddressBits)
980+
let unmanaged = Unmanaged<__SharedStringStorage>.fromOpaque(largeAddress)
981+
return unmanaged.takeUnretainedValue()
961982
#endif
962983
}
963984

@@ -970,7 +991,8 @@ extension _StringObject {
970991
return object
971992
#else
972993
_internalInvariant(largeIsCocoa && !isImmortal)
973-
return Builtin.reinterpretCast(largeAddressBits)
994+
let unmanaged = Unmanaged<AnyObject>.fromOpaque(largeAddress)
995+
return unmanaged.takeUnretainedValue()
974996
#endif
975997
}
976998

@@ -979,7 +1001,8 @@ extension _StringObject {
9791001
@inline(__always)
9801002
internal var owner: AnyObject? {
9811003
guard self.isMortal else { return nil }
982-
return Builtin.reinterpretCast(largeAddressBits)
1004+
let unmanaged = Unmanaged<AnyObject>.fromOpaque(largeAddress)
1005+
return unmanaged.takeUnretainedValue()
9831006
}
9841007
}
9851008

@@ -1058,7 +1081,8 @@ extension _StringObject {
10581081
@inline(__always)
10591082
internal var objCBridgeableObject: AnyObject {
10601083
_internalInvariant(hasObjCBridgeableObject)
1061-
return Builtin.reinterpretCast(largeAddressBits)
1084+
let unmanaged = Unmanaged<AnyObject>.fromOpaque(largeAddress)
1085+
return unmanaged.takeUnretainedValue()
10621086
}
10631087

10641088
// Whether the object provides fast UTF-8 contents that are nul-terminated
@@ -1222,7 +1246,8 @@ extension _StringObject {
12221246
}
12231247
}
12241248
if _countAndFlags.isNativelyStored {
1225-
let anyObj = Builtin.reinterpretCast(largeAddressBits) as AnyObject
1249+
let unmanaged = Unmanaged<AnyObject>.fromOpaque(largeAddress)
1250+
let anyObj = unmanaged.takeUnretainedValue()
12261251
_internalInvariant(anyObj is __StringStorage)
12271252
}
12281253
}

stdlib/public/core/StringStorage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ extension _StringGuts {
735735

736736
let mutPtr: UnsafeMutablePointer<_StringBreadcrumbs?>
737737
if hasNativeStorage {
738-
mutPtr = _object.nativeStorage._breadcrumbsAddress
738+
mutPtr = _object.withNativeStorage { $0._breadcrumbsAddress }
739739
} else {
740740
mutPtr = UnsafeMutablePointer(
741741
Builtin.addressof(&_object.sharedStorage._breadcrumbs))

0 commit comments

Comments
 (0)