Skip to content

Commit 1c0a367

Browse files
committed
[String] Fix crash when given null UBP
1 parent 56ddbb1 commit 1c0a367

File tree

5 files changed

+68
-7
lines changed

5 files changed

+68
-7
lines changed

stdlib/public/core/ContiguouslyStored.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ extension UnsafeBufferPointer: _HasContiguousBytes {
4242
func withUnsafeBytes<R>(
4343
_ body: (UnsafeRawBufferPointer) throws -> R
4444
) rethrows -> R {
45-
let ptr = UnsafeRawPointer(self.baseAddress._unsafelyUnwrappedUnchecked)
45+
let ptr = UnsafeRawPointer(self.baseAddress)
4646
let len = self.count &* MemoryLayout<Element>.stride
4747
return try body(UnsafeRawBufferPointer(start: ptr, count: len))
4848
}
@@ -52,7 +52,7 @@ extension UnsafeMutableBufferPointer: _HasContiguousBytes {
5252
func withUnsafeBytes<R>(
5353
_ body: (UnsafeRawBufferPointer) throws -> R
5454
) rethrows -> R {
55-
let ptr = UnsafeRawPointer(self.baseAddress._unsafelyUnwrappedUnchecked)
55+
let ptr = UnsafeRawPointer(self.baseAddress)
5656
let len = self.count &* MemoryLayout<Element>.stride
5757
return try body(UnsafeRawBufferPointer(start: ptr, count: len))
5858
}

stdlib/public/core/SmallString.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ extension _SmallString {
242242
// Direct from UTF-8
243243
@inlinable @inline(__always)
244244
internal init?(_ input: UnsafeBufferPointer<UInt8>) {
245+
if input.isEmpty {
246+
self.init()
247+
return
248+
}
249+
245250
let count = input.count
246251
guard count <= _SmallString.capacity else { return nil }
247252

stdlib/public/core/String.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,10 +405,9 @@ extension String {
405405
contigBytes._providesContiguousBytesNoCopy
406406
{
407407
self = contigBytes.withUnsafeBytes { rawBufPtr in
408-
let ptr = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked
409408
return String._fromUTF8Repairing(
410409
UnsafeBufferPointer(
411-
start: ptr.assumingMemoryBound(to: UInt8.self),
410+
start: rawBufPtr.baseAddress?.assumingMemoryBound(to: UInt8.self),
412411
count: rawBufPtr.count)).0
413412
}
414413
return
@@ -836,7 +835,7 @@ extension String {
836835
}
837836
return codeUnits
838837
}
839-
838+
840839
public // @testable
841840
func _withNFCCodeUnits(_ f: (UInt8) throws -> Void) rethrows {
842841
try _gutsSlice._withNFCCodeUnits(f)

stdlib/public/core/StringCreate.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
internal func _allASCII(_ input: UnsafeBufferPointer<UInt8>) -> Bool {
16+
if input.isEmpty { return true }
17+
1618
// NOTE: Avoiding for-in syntax to avoid bounds checks
1719
//
1820
// TODO(String performance): SIMD-ize
@@ -146,9 +148,8 @@ extension String {
146148
_ body: (UnsafeBufferPointer<UTF8.CodeUnit>) throws -> R
147149
) rethrows -> R {
148150
return try self.withUnsafeBytes { rawBufPtr in
149-
let rawPtr = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked
150151
return try body(UnsafeBufferPointer(
151-
start: rawPtr.assumingMemoryBound(to: UInt8.self),
152+
start: rawBufPtr.baseAddress?.assumingMemoryBound(to: UInt8.self),
152153
count: rawBufPtr.count))
153154
}
154155
}

test/stdlib/StringCreate.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %target-run-simple-swift
2+
// REQUIRES: executable_test
3+
4+
import StdlibUnittest
5+
defer { runAllTests() }
6+
7+
var StringCreateTests = TestSuite("StringCreateTests")
8+
9+
enum SimpleString: String {
10+
case smallASCII = "abcdefg"
11+
case smallUnicode = "abéÏ𓀀"
12+
case largeASCII = "012345678901234567890"
13+
case largeUnicode = "abéÏ012345678901234567890𓀀"
14+
case emoji = "😀😃🤢🤮👩🏿‍🎤🧛🏻‍♂️🧛🏻‍♂️👩‍👩‍👦‍👦"
15+
}
16+
17+
let simpleStrings: [String] = [
18+
SimpleString.smallASCII.rawValue,
19+
SimpleString.smallUnicode.rawValue,
20+
SimpleString.largeASCII.rawValue,
21+
SimpleString.largeUnicode.rawValue,
22+
SimpleString.emoji.rawValue,
23+
"",
24+
]
25+
26+
extension String {
27+
var utf32: [UInt32] { return unicodeScalars.map { $0.value } }
28+
}
29+
30+
StringCreateTests.test("String(decoding:as)") {
31+
func validateDecodingAs(_ str: String) {
32+
// Non-contiguous (maybe) storage
33+
expectEqual(str, String(decoding: str.utf8, as: UTF8.self))
34+
expectEqual(str, String(decoding: str.utf16, as: UTF16.self))
35+
expectEqual(str, String(decoding: str.utf32, as: UTF32.self))
36+
37+
// Contiguous storage
38+
expectEqual(str, String(decoding: Array(str.utf8), as: UTF8.self))
39+
expectEqual(str, String(decoding: Array(str.utf16), as: UTF16.self))
40+
expectEqual(str, String(decoding: Array(str.utf32), as: UTF32.self))
41+
42+
}
43+
44+
for str in simpleStrings {
45+
validateDecodingAs(str)
46+
}
47+
48+
// Corner-case: UBP with null pointer (https://bugs.swift.org/browse/SR-9869)
49+
expectEqual(
50+
"", String(decoding: UnsafeBufferPointer(_empty: ()), as: UTF8.self))
51+
expectEqual(
52+
"", String(decoding: UnsafeBufferPointer(_empty: ()), as: UTF16.self))
53+
expectEqual(
54+
"", String(decoding: UnsafeBufferPointer(_empty: ()), as: UTF32.self))
55+
}
56+

0 commit comments

Comments
 (0)