Skip to content

Commit f3a7a34

Browse files
authored
Fix buffer overflow in _NSCFString.getBytes (#5287)
* Fix buffer overflow in _NSCFString.getBytes * Fix unit test failure * Fix test_dateParseAndFormatWithJapaneseCalendar
1 parent edfa17d commit f3a7a34

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

Sources/Foundation/NSCFString.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,19 +154,25 @@ internal func _CFSwiftStringGetBytes(_ str: AnyObject, encoding: CFStringEncodin
154154
// TODO: Don't treat many encodings like they are UTF8
155155
case CFStringEncoding(kCFStringEncodingUTF8), CFStringEncoding(kCFStringEncodingISOLatin1), CFStringEncoding(kCFStringEncodingMacRoman), CFStringEncoding(kCFStringEncodingASCII), CFStringEncoding(kCFStringEncodingNonLossyASCII):
156156
let encodingView = (str as! NSString).substring(with: NSRange(range)).utf8
157-
if let buffer = buffer {
158-
for (idx, character) in encodingView.enumerated() {
157+
var converted = 0
158+
for (idx, character) in encodingView.enumerated() {
159+
if encoding == CFStringEncoding(kCFStringEncodingASCII) && !Unicode.ASCII.isASCII(character) { break }
160+
if let buffer, maxBufLen > 0 {
161+
if idx >= maxBufLen { break }
159162
buffer.advanced(by: idx).initialize(to: character)
160163
}
164+
converted += 1
161165
}
162-
usedBufLen?.pointee = encodingView.count
163-
convertedLength = encodingView.count
166+
usedBufLen?.pointee = converted
167+
convertedLength = converted
164168

165169
case CFStringEncoding(kCFStringEncodingUTF16):
166170
let encodingView = (str as! NSString)._swiftObject.utf16
167171
let start = encodingView.startIndex
172+
var converted = 0
168173
if let buffer = buffer {
169174
for idx in 0..<range.length {
175+
if idx * 2 >= maxBufLen { break }
170176
// Since character is 2 bytes but the buffer is in term of 1 byte values, we have to split it up
171177
let character = encodingView[encodingView.index(start, offsetBy: idx + range.location)]
172178
#if _endian(big)
@@ -178,11 +184,12 @@ internal func _CFSwiftStringGetBytes(_ str: AnyObject, encoding: CFStringEncodin
178184
#endif
179185
buffer.advanced(by: idx * 2).initialize(to: byte0)
180186
buffer.advanced(by: (idx * 2) + 1).initialize(to: byte1)
187+
converted += 1
181188
}
182189
}
183190
// Every character was 2 bytes
184-
usedBufLen?.pointee = range.length * 2
185-
convertedLength = range.length
191+
usedBufLen?.pointee = converted * 2
192+
convertedLength = converted
186193

187194
default:
188195
fatalError("Attempted to get bytes of a Swift string using an unsupported encoding")

Tests/Foundation/TestPropertyListSerialization.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
import CoreFoundation
11+
1012
class TestPropertyListSerialization : XCTestCase {
1113
func test_BasicConstruction() {
1214
let dict = NSMutableDictionary(capacity: 0)
@@ -80,4 +82,33 @@ class TestPropertyListSerialization : XCTestCase {
8082
XCTAssertEqual(nserror.userInfo[NSDebugDescriptionErrorKey] as? String, "Cannot parse a NULL or zero-length data")
8183
}
8284
}
85+
86+
func test_decodeOverflowUnicodeString() throws {
87+
var native = "ФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФ"
88+
let ns = native.withCString {
89+
NSString(utf8String: $0)!
90+
}
91+
do {
92+
var buffer = Array<UInt8>(repeating: 1, count: native.utf8.count)
93+
buffer.withUnsafeMutableBufferPointer { bufferPtr in
94+
var used: CFIndex = 0
95+
CFStringGetBytes(unsafeBitCast(ns, to: CFString.self), CFRangeMake(0, ns.length), CFStringBuiltInEncodings.UTF8.rawValue, 0xF, false, bufferPtr.baseAddress!, 10, &used)
96+
XCTAssertEqual(used, 10)
97+
}
98+
for i in 10 ..< buffer.count {
99+
XCTAssertEqual(buffer[i], 1)
100+
}
101+
}
102+
do {
103+
var buffer = Array<UInt16>(repeating: 1, count: native.utf8.count)
104+
buffer.withUnsafeMutableBufferPointer { bufferPtr in
105+
var used: CFIndex = 0
106+
CFStringGetBytes(unsafeBitCast(ns, to: CFString.self), CFRangeMake(0, ns.length), CFStringBuiltInEncodings.UTF16.rawValue, 0xF, false, bufferPtr.baseAddress!, 10, &used)
107+
XCTAssertEqual(used, 10)
108+
}
109+
for i in 10 ..< buffer.count {
110+
XCTAssertEqual(buffer[i], 1)
111+
}
112+
}
113+
}
83114
}

0 commit comments

Comments
 (0)