Skip to content

Commit 25380a1

Browse files
committed
Mitigate misuse of pointer conversion in String inits
1 parent 74eb6f4 commit 25380a1

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

Sources/System/PlatformString.swift

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,56 @@ extension String {
2424
self.init(_errorCorrectingPlatformString: platformString)
2525
}
2626

27+
/// Creates a string by interpreting the null-terminated platform string as
28+
/// UTF-8 on Unix and UTF-16 on Windows.
29+
///
30+
/// - Parameter platformString: The null-terminated platform string to be
31+
/// interpreted as `CInterop.PlatformUnicodeEncoding`.
32+
///
33+
/// - Note It is a precondition that `platformString` must be null-terminated.
34+
/// The absence of a null byte will trigger a runtime error.
35+
///
36+
/// If the content of the platform string isn't well-formed Unicode,
37+
/// this initializer replaces invalid bytes with U+FFFD.
38+
/// This means that, depending on the semantics of the specific platform,
39+
/// conversion to a string and back might result in a value that's different
40+
/// from the original platform string.
41+
@inlinable
42+
@_alwaysEmitIntoClient
43+
public init(platformString: [CInterop.PlatformChar]) {
44+
guard let _ = platformString.firstIndex(of: 0) else {
45+
fatalError(
46+
"input of String.init(platformString:) must be null-terminated"
47+
)
48+
}
49+
self = platformString.withUnsafeBufferPointer {
50+
String(platformString: $0.baseAddress!)
51+
}
52+
}
53+
54+
@available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)")
55+
@inlinable
56+
@_alwaysEmitIntoClient
57+
public init(platformString: inout CInterop.PlatformChar) {
58+
guard platformString == 0 else {
59+
fatalError(
60+
"input of String.init(platformString:) must be null-terminated"
61+
)
62+
}
63+
self = ""
64+
}
65+
66+
@available(*, deprecated, message: "Use a copy of the String argument")
67+
@inlinable
68+
@_alwaysEmitIntoClient
69+
public init(platformString: String) {
70+
if let nullLoc = platformString.firstIndex(of: "\0") {
71+
self = String(platformString[..<nullLoc])
72+
} else {
73+
self = platformString
74+
}
75+
}
76+
2777
/// Creates a string by interpreting the null-terminated platform string as
2878
/// UTF-8 on Unix and UTF-16 on Windows.
2979
///
@@ -38,6 +88,62 @@ extension String {
3888
self.init(_platformString: platformString)
3989
}
4090

91+
/// Creates a string by interpreting the null-terminated platform string as
92+
/// UTF-8 on Unix and UTF-16 on Windows.
93+
///
94+
/// - Parameter platformString: The null-terminated platform string to be
95+
/// interpreted as `CInterop.PlatformUnicodeEncoding`.
96+
///
97+
/// - Note It is a precondition that `platformString` must be null-terminated.
98+
/// The absence of a null byte will trigger a runtime error.
99+
///
100+
/// If the contents of the platform string isn't well-formed Unicode,
101+
/// this initializer returns `nil`.
102+
@inlinable
103+
@_alwaysEmitIntoClient
104+
public init?(
105+
validatingPlatformString platformString: [CInterop.PlatformChar]
106+
) {
107+
guard let _ = platformString.firstIndex(of: 0) else {
108+
fatalError(
109+
"input of String.init(validatingPlatformString:) must be null-terminated"
110+
)
111+
}
112+
guard let string = platformString.withUnsafeBufferPointer({
113+
String(validatingPlatformString: $0.baseAddress!)
114+
}) else {
115+
return nil
116+
}
117+
self = string
118+
}
119+
120+
@available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)")
121+
@inlinable
122+
@_alwaysEmitIntoClient
123+
public init?(
124+
validatingPlatformString platformString: inout CInterop.PlatformChar
125+
) {
126+
guard platformString == 0 else {
127+
fatalError(
128+
"input of String.init(validatingPlatformString:) must be null-terminated"
129+
)
130+
}
131+
self = ""
132+
}
133+
134+
@available(*, deprecated, message: "Use a copy of the String argument")
135+
@inlinable
136+
@_alwaysEmitIntoClient
137+
public init?(
138+
validatingPlatformString platformString: String
139+
) {
140+
if let nullLoc = platformString.firstIndex(of: "\0") {
141+
self = String(platformString[..<nullLoc])
142+
} else {
143+
self = platformString
144+
}
145+
}
146+
41147
/// Calls the given closure with a pointer to the contents of the string,
42148
/// represented as a null-terminated platform string.
43149
///

0 commit comments

Comments
 (0)