Skip to content

Commit f12ec24

Browse files
authored
rdar://133742708 (JSONDecoder crashes whole app on invalid input instead of throwing Error) (#1043)
1 parent 221a0dd commit f12ec24

File tree

2 files changed

+18
-8
lines changed

2 files changed

+18
-8
lines changed

Sources/FoundationEssentials/JSON/JSONDecoder.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -786,8 +786,8 @@ extension JSONDecoderImpl: Decoder {
786786

787787
var iter = jsonMap.makeObjectIterator(from: region.startOffset)
788788
while let (keyValue, value) = iter.next() {
789-
// Failing to unwrap a string here is impossible, as scanning already guarantees that dictionary keys are strings.
790-
let key = try! self.unwrapString(from: keyValue, for: dictCodingPathNode, _CodingKey?.none)
789+
// We know these values are keys, but UTF-8 decoding could still fail.
790+
let key = try self.unwrapString(from: keyValue, for: dictCodingPathNode, _CodingKey?.none)
791791
let value = try self.unwrap(value, as: dictType.elementType, for: dictCodingPathNode, _CodingKey(stringValue: key)!)
792792
result[key]._setIfNil(to: value)
793793
}
@@ -1202,14 +1202,14 @@ extension JSONDecoderImpl {
12021202
switch keyDecodingStrategy {
12031203
case .useDefaultKeys:
12041204
while let (keyValue, value) = iter.next() {
1205-
// Failing to unwrap a string here is impossible, as scanning already guarantees that dictionary keys are strings.
1206-
let key = try! impl.unwrapString(from: keyValue, for: codingPathNode, _CodingKey?.none)
1205+
// We know these values are keys, but UTF-8 decoding could still fail.
1206+
let key = try impl.unwrapString(from: keyValue, for: codingPathNode, _CodingKey?.none)
12071207
result[key]._setIfNil(to: value)
12081208
}
12091209
case .convertFromSnakeCase:
12101210
while let (keyValue, value) = iter.next() {
1211-
// Failing to unwrap a string here is impossible, as scanning already guarantees that dictionary keys are strings.
1212-
let key = try! impl.unwrapString(from: keyValue, for: codingPathNode, _CodingKey?.none)
1211+
// We know these values are keys, but UTF-8 decoding could still fail.
1212+
let key = try impl.unwrapString(from: keyValue, for: codingPathNode, _CodingKey?.none)
12131213

12141214
// Convert the snake case keys in the container to camel case.
12151215
// If we hit a duplicate key after conversion, then we'll use the first one we saw.
@@ -1219,8 +1219,8 @@ extension JSONDecoderImpl {
12191219
case .custom(let converter):
12201220
let codingPathForCustomConverter = codingPathNode.path
12211221
while let (keyValue, value) = iter.next() {
1222-
// Failing to unwrap a string here is impossible, as scanning already guarantees that dictionary keys are strings.
1223-
let key = try! impl.unwrapString(from: keyValue, for: codingPathNode, _CodingKey?.none)
1222+
// We know these values are keys, but UTF-8 decoding could still fail.
1223+
let key = try impl.unwrapString(from: keyValue, for: codingPathNode, _CodingKey?.none)
12241224

12251225
var pathForKey = codingPathForCustomConverter
12261226
pathForKey.append(_CodingKey(stringValue: key)!)

Tests/FoundationEssentialsTests/JSONEncoderTests.swift

+10
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,16 @@ final class JSONEncoderTests : XCTestCase {
16411641

16421642
XCTAssertThrowsError(try decoder.decode(String.self, from: utf8_BOM + json.data(using: String._Encoding.utf16BigEndian)!))
16431643
}
1644+
1645+
func test_invalidKeyUTF8() {
1646+
// {"key[255]":"value"}
1647+
// The invalid UTF-8 byte sequence in the key should trigger a thrown error, not a crash.
1648+
let data = Data([123, 34, 107, 101, 121, 255, 34, 58, 34, 118, 97, 108, 117, 101, 34, 125])
1649+
struct Example: Decodable {
1650+
let key: String
1651+
}
1652+
XCTAssertThrowsError(try JSONDecoder().decode(Example.self, from: data))
1653+
}
16441654

16451655
func test_valueNotFoundError() {
16461656
struct ValueNotFound : Decodable {

0 commit comments

Comments
 (0)