Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions wire-runtime-swift/src/main/swift/ProtoCodable/ProtoReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,7 @@ public final class ProtoReader {
throw ProtoDecoder.Error.unexpectedEndGroupFieldNumber(expected: nil, found: tag)

case .lengthDelimited:
let length = try Int32(truncatingIfNeeded: buffer.readVarint())
state = .lengthDelimited(length: Int(length))
state = .lengthDelimited(length: try readLengthDelimitedLength(tag: tag))
currentTag = tag
return tag

Expand Down Expand Up @@ -793,8 +792,7 @@ public final class ProtoReader {
return

case .lengthDelimited:
let length = try Int32(truncatingIfNeeded: buffer.readVarint())
state = .lengthDelimited(length: Int(length))
state = .lengthDelimited(length: try readLengthDelimitedLength(tag: tag))
let data = try readData()
try unknownFieldsWriter.encode(tag: tag, value: data)

Expand Down Expand Up @@ -856,6 +854,16 @@ public final class ProtoReader {
return (tag, wireType)
}

private func readLengthDelimitedLength(tag: UInt32) throws -> Int {
let length = try Int32(truncatingIfNeeded: buffer.readVarint())
if length < 0 {
throw ProtoDecoder.Error.invalidStructure(
message: "Negative length: \(length). Reader position: \(buffer.position). Last read tag: \(tag)."
)
}
return Int(length)
}

// MARK: - Private Methods - Decoding - Repeated Field

private func decode<T>(into array: inout [T], decode: () throws -> T?) throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ final class ReadBuffer {
}

func verifyAdditional(count: Int) throws {
guard pointer.advanced(by: count) <= end else {
guard count >= 0, pointer.advanced(by: count) <= end else {
throw ProtoDecoder.Error.unexpectedEndOfData
}
}
Expand Down
44 changes: 44 additions & 0 deletions wire-runtime-swift/src/test/swift/ProtoReaderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,42 @@ final class ProtoReaderTests: XCTestCase {
}
}

func testLengthDelimitedRejectsNegativeLength() throws {
let data = Foundation.Data(hexEncoded: """
0A // (Tag 1 | Length Delimited)
80FFFFFF0F // Length -128
""")!

XCTAssertThrowsError(
try test(data: data) { reader in
_ = try reader.forEachTag { _ in
XCTFail("The negative length should have thrown before returning a tag")
}
}
) { error in
assertNegativeLengthError(error, readerPosition: 6)
}
}

func testSkipGroupRejectsNegativeLengthDelimited() throws {
let data = Foundation.Data(hexEncoded: """
9B06 // (Tag 99 | Start Group)
0A // (Tag 1 | Length Delimited)
80FFFFFF0F // Length -128
9C06 // (Tag 99 | End Group)
""")!

XCTAssertThrowsError(
try test(data: data) { reader in
_ = try reader.forEachTag { _ in
XCTFail("The group should have been skipped or rejected")
}
}
) { error in
assertNegativeLengthError(error, readerPosition: 8)
}
}

// MARK: - Tests - Unknown Fields

func testUnknownFields() throws {
Expand Down Expand Up @@ -1330,6 +1366,14 @@ final class ProtoReaderTests: XCTestCase {
try test(reader)
}
}

private func assertNegativeLengthError(_ error: Error, readerPosition: Int) {
guard case let ProtoDecoder.Error.invalidStructure(message) = error else {
XCTFail("Unexpected error: \(error)")
return
}
XCTAssertEqual(message, "Negative length: -128. Reader position: \(readerPosition). Last read tag: 1.")
}
}

// MARK: -
Expand Down
Loading