Skip to content

Commit f139268

Browse files
committed
JSONSerialization: fix a crash when an escape sequence contains low-surrogate code point without the corresponding high-surrogate code point
1 parent b5d216e commit f139268

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

Foundation/JSONSerialization.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,11 +738,20 @@ private struct JSONReader {
738738
return nil
739739
}
740740

741-
if !UTF16.isLeadSurrogate(codeUnit) {
741+
let isLeadSurrogate = UTF16.isLeadSurrogate(codeUnit)
742+
let isTrailSurrogate = UTF16.isTrailSurrogate(codeUnit)
743+
744+
if !isLeadSurrogate && !isTrailSurrogate {
742745
return (String(UnicodeScalar(codeUnit)!), index)
743746
}
744747

745-
guard let (trailCodeUnit, finalIndex) = try consumeASCIISequence("\\u", input: index).flatMap(parseCodeUnit) , UTF16.isTrailSurrogate(trailCodeUnit) else {
748+
guard !isTrailSurrogate else {
749+
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: [
750+
"NSDebugDescription" : "Unable to convert unicode escape sequence (no high-surrogate code point) to UTF8-encoded character at position \(source.distanceFromStart(input))"
751+
])
752+
}
753+
754+
guard let (trailCodeUnit, finalIndex) = try consumeASCIISequence("\\u", input: index).flatMap(parseCodeUnit), UTF16.isTrailSurrogate(trailCodeUnit) else {
746755
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: [
747756
"NSDebugDescription" : "Unable to convert unicode escape sequence (no low-surrogate code point) to UTF8-encoded character at position \(source.distanceFromStart(input))"
748757
])

TestFoundation/TestJSONSerialization.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ extension TestJSONSerialization {
115115
("test_deserialize_invalidValueInArray_withData", test_deserialize_invalidValueInArray_withData),
116116
("test_deserialize_badlyFormedArray_withData", test_deserialize_badlyFormedArray_withData),
117117
("test_deserialize_invalidEscapeSequence_withData", test_deserialize_invalidEscapeSequence_withData),
118+
("test_deserialize_unicodeMissingLeadingSurrogate_withData", test_deserialize_unicodeMissingLeadingSurrogate_withData),
118119
("test_deserialize_unicodeMissingTrailingSurrogate_withData", test_deserialize_unicodeMissingTrailingSurrogate_withData),
119120

120121
//Deserialization with Stream
@@ -146,6 +147,7 @@ extension TestJSONSerialization {
146147
("test_deserialize_invalidValueInArray_withStream", test_deserialize_invalidValueInArray_withStream),
147148
("test_deserialize_badlyFormedArray_withStream", test_deserialize_badlyFormedArray_withStream),
148149
("test_deserialize_invalidEscapeSequence_withStream", test_deserialize_invalidEscapeSequence_withStream),
150+
("test_deserialize_unicodeMissingLeadingSurrogate_withStream", test_deserialize_unicodeMissingLeadingSurrogate_withStream),
149151
("test_deserialize_unicodeMissingTrailingSurrogate_withStream", test_deserialize_unicodeMissingTrailingSurrogate_withStream),
150152
("test_JSONObjectWithStream_withFile", test_JSONObjectWithStream_withFile),
151153
("test_JSONObjectWithStream_withURL", test_JSONObjectWithStream_withURL),
@@ -242,6 +244,10 @@ extension TestJSONSerialization {
242244
deserialize_invalidEscapeSequence(objectType: .data)
243245
}
244246

247+
func test_deserialize_unicodeMissingLeadingSurrogate_withData() {
248+
deserialize_unicodeMissingLeadingSurrogate(objectType: .data)
249+
}
250+
245251
func test_deserialize_unicodeMissingTrailingSurrogate_withData() {
246252
deserialize_unicodeMissingTrailingSurrogate(objectType: .data)
247253
}
@@ -336,6 +342,10 @@ extension TestJSONSerialization {
336342
deserialize_invalidEscapeSequence(objectType: .stream)
337343
}
338344

345+
func test_deserialize_unicodeMissingLeadingSurrogate_withStream() {
346+
deserialize_unicodeMissingLeadingSurrogate(objectType: .stream)
347+
}
348+
339349
func test_deserialize_unicodeMissingTrailingSurrogate_withStream() {
340350
deserialize_unicodeMissingTrailingSurrogate(objectType: .stream)
341351
}
@@ -738,6 +748,20 @@ extension TestJSONSerialization {
738748
}
739749
}
740750

751+
func deserialize_unicodeMissingLeadingSurrogate(objectType: ObjectType) {
752+
let subject = "[\"\\uDFF3\"]"
753+
do {
754+
guard let data = subject.data(using: .utf8) else {
755+
XCTFail("Unable to convert string to data")
756+
return
757+
}
758+
let _ = try getjsonObjectResult(data, objectType) as? [String]
759+
XCTFail("Expected error: Missing Leading Surrogate")
760+
} catch {
761+
// Passing case; the unicode character is malformed
762+
}
763+
}
764+
741765
func deserialize_unicodeMissingTrailingSurrogate(objectType: ObjectType) {
742766
let subject = "[\"\\uD834\"]"
743767
do {

0 commit comments

Comments
 (0)