Summary
Wire: Unauthenticated decoder crash via 32-bit length integer overflow in ByteArrayProtoReader32 (incomplete fix of CVE-2026-45799)
Wire's protobuf decoders did not consistently validate attacker-controlled length-delimited sizes against the current reader bounds before computing cursor, limit, or pointer positions.
In the Kotlin runtime, ProtoAdapter.decode(ByteArray) and ProtoAdapter.decode(ByteString) use the ProtoReader32 fast path implemented by ByteArrayProtoReader32. In ByteArrayProtoReader32.internalNextLengthDelimited(), Wire read an untrusted varint length into an Int and rejected only negative values. A length such as 2147483647 is non-negative, so it passed that check, but pos + length overflowed the signed 32-bit cursor and produced a negative limit. The following if (limit > pushedLimit) guard did not catch this because the overflowed value was negative.
That invalid limit then reached string, bytes, skip, and scalar-reading paths as an invalid byte count or invalid range. Instead of failing as a checked decode error such as IOException, malformed input could throw unchecked runtime exceptions including IllegalArgumentException and ArrayIndexOutOfBoundsException. Applications commonly treat malformed protobuf input as an expected decode failure; unchecked runtime exceptions escaping that boundary can crash request handling or the process.
The original report is a sibling of the negative-length skipped-group bug fixed as CVE-2026-45799. It is not the same bug. The length in this advisory is positive, and the overflow occurs when setting a length-delimited message limit, not only when skipping a group.
While auditing for the same bug class, related boundary flaws were also found and fixed:
- Kotlin
ProtoReadernow validates logical message limits before varint, fixed32, fixed64, and skip operations. The originally reported byte-array overflow payload did not reproduce as the same signed overflow inProtoReader, because that reader tracks positions asLong, but the streaming reader still needed consistent current-message-limit enforcement. - Swift
ReadBuffer.readVarint()readpointer.pointeebefore checking that one byte remained. A tag-only varint field could read past the end of the buffer. - Swift
ReadBuffer.verifyAdditional(count:)formedpointer.advanced(by: count)before proving the requestedcountfit within the remaining buffer, so pointer arithmetic ran before the bounds were established. (The distinct Swift negative-lengthskipGroup()crash is tracked separately as GHSA-86wm-r4c5-2rc9 / CVE-2026-61695; this advisory covers the positive/oversized-length boundary failures.) - Swift nested-message decoding and packed-repeated decoding computed end pointers from untrusted lengths before validating that the bytes were present.
- Swift packed-repeated decoding reserved array capacity from an untrusted length before validating that the length existed in the current buffer.
- Swift size-delimited decoding converted an untrusted
UInt64varint size toIntwithout exactness or availability checks. On platforms where the value is not representable, this could trap.
The fix enforces a single invariant across the hardened readers: every decoded or skipped byte count must be non-negative and no larger than the remaining bytes in the current logical message limit before any cursor, pointer, limit, allocation, or slice is advanced.
Proof of concept and regression payloads
These payloads are intentionally small and should be treated as malformed protobuf input. After the fix, they must fail with normal decode errors such as IOException, EOFException, or ProtoDecoder.Error.unexpectedEndOfData, not unchecked runtime exceptions, traps, out-of-bounds reads, or large allocations.
Kotlin byte-array known length-delimited field
Hex:
0A FF FF FF FF 07
Meaning:
0A: field 1, length-delimitedFF FF FF FF 07: varint length2147483647
Pre-fix behavior observed through Person.ADAPTER.decode(byteArray):
java.lang.IllegalArgumentException: startIndex: 6 > endIndex: -2147483643
Expected fixed behavior:
IOException / EOFException
Kotlin byte-array unknown length-delimited field
Hex:
1A FF FF FF FF 07
Meaning:
1A: field 3, length-delimitedFF FF FF FF 07: varint length2147483647
Pre-fix behavior observed:
ArrayIndexOutOfBoundsException
Expected fixed behavior:
IOException / EOFException
Kotlin byte-array skipped group containing oversized positive length
Hex:
0B 0A FF FF FF FF 07 0C
Meaning:
0B: start group, field 10A: nested field 1, length-delimitedFF FF FF FF 07: varint length21474836470C: end group, field 1
Expected fixed behavior:
IOException / EOFException
Kotlin current-message-limit fixed32 boundary
Hex:
02 0D 05 00 00 00
Meaning:
02: outer length-delimited message length is 2 bytes0D: nested field 1, fixed3205 00 00 00: enough bytes remain in the underlying source, but not inside the current logical message limit
Expected fixed behavior:
EOFException
This covers the invariant that scalar reads must not cross the current length-delimited message boundary even when the underlying source has more bytes available.
Swift tag-only varint value
Hex:
08
Meaning:
08: field 1, varint- Missing varint value byte
Pre-fix risk:
ReadBuffer.readVarint()could dereferencepointer.pointeebefore verifying that a byte remained.
Expected fixed behavior:
ProtoDecoder.Error.unexpectedEndOfData
Swift nested message with oversized positive length
Hex:
12 FF FF FF FF 07
Meaning:
12: field 2, length-delimitedFF FF FF FF 07: varint length2147483647
Pre-fix risk:
- Nested message decoding computed an end pointer from an untrusted length before proving that the buffer contained that many bytes.
Expected fixed behavior:
ProtoDecoder.Error.unexpectedEndOfData
Swift packed repeated field with oversized positive length
Hex:
0A FF FF FF FF 07
Meaning:
0A: field 1, length-delimited packed repeated fieldFF FF FF FF 07: varint length2147483647
Pre-fix risk:
- Packed repeated decoding could reserve capacity based on an untrusted length before proving the bytes were present.
Expected fixed behavior:
ProtoDecoder.Error.unexpectedEndOfData
Swift size-delimited stream with unrepresentable size
Hex:
FF FF FF FF FF FF FF FF FF 01
Meaning:
- Size-delimited message length varint
UInt64.max
Pre-fix risk:
ProtoDecoder.decodeSizeDelimited(_:from:)converted the untrustedUInt64toIntwithout exactness checking.
Expected fixed behavior:
ProtoDecoder.Error.unexpectedEndOfData
Root cause
The vulnerable code mixed three operations that must remain separate:
- Decode an untrusted protobuf length.
- Validate that the length is non-negative and fits within the current logical message boundary.
- Advance the cursor, pointer, limit, slice, or allocation based on that length.
In the vulnerable paths, step 3 happened before step 2 was complete. For Kotlin ByteArrayProtoReader32, this caused signed integer wraparound in pos + length. For Swift, related pointer and allocation operations could be performed before proving the requested bytes existed.
Workarounds
The recommended remediation is to upgrade to a patched release.
Partial mitigations if an immediate upgrade is not possible:
- Reject or cap untrusted protobuf message sizes before passing bytes to Wire.
- Prefer decoding from a bounded source where possible rather than decoding unbounded attacker-controlled byte arrays.
- Treat unchecked runtime exceptions from protobuf decode as malformed-input failures at service trust boundaries so they cannot crash the process.
- For Swift, do not pass untrusted size-delimited streams or
Datadirectly to affected decoders without an outer size cap and exception/error boundary.
These mitigations reduce exposure but do not fully fix the parser bugs.
Detection
A crash or error may contain one of the following symptoms when processing malformed protobuf bytes:
IllegalArgumentException: startIndex: 6 > endIndex: -2147483643
ArrayIndexOutOfBoundsException
IndexOutOfBoundsException
unexpected unchecked RuntimeException during ProtoAdapter.decode(ByteArray)
Swift trap during Int conversion from an untrusted protobuf size
Swift unexpected pointer/buffer failure while reading malformed varints or length-delimited values
The absence of these exact messages does not prove safety. Any unchecked exception, trap, or process crash while decoding malformed length-delimited protobuf input should be investigated.
Verification
Regression tests added:
ProtoReader32Test.lengthDelimitedRejectsPositiveLengthOverflowProtoReader32Test.fixed32CannotReadPastLengthDelimitedLimitProtoReaderTest.fixed32CannotReadPastLengthDelimitedLimitProtoReaderTests.testReadVarintRejectsMissingValueProtoReaderTests.testNestedMessageRejectsOversizedLengthProtoReaderTests.testPackedRepeatedRejectsOversizedLengthBeforePreallocationProtoDecoderTests.testDecodeSizeDelimitedRejectsUnrepresentableSize
Focused verification command:
./gradlew :wire-runtime:jvmTest :wire-runtime-swift:test
Expected result:
BUILD SUCCESSFUL
Relationship to related advisories
This is a distinct vulnerability from the negative-length issues. It is a
different bug class, a positive, non-negative length (for example2147483647) that passes the existing length < 0 check but still overflows
the signed 32-bit cursor or crosses the current message boundary, and it has
a separate fix (PR #3635, not the negative-length PRs).
CVE-2026-45799/GHSA-7xpr-hc2w-34m9fixed the original Kotlin/JVM
negative-length skipped-group crash (Wire6.3.0). The non-negative
overflow described here was not covered by that check and remained
exploitable through6.4.4.GHSA-86wm-r4c5-2rc9/CVE-2026-61695covers the Swift negative-lengthskipGroup()crash (PR #3616). The Swift hardening in this advisory
(PR #3635) instead addresses positive/oversized-length overflow, buffer
over-read, and unrepresentable-size conversions in the Swift readers.
Impact
An attacker who can supply protobuf bytes to an application using affected Wire decoders can trigger a denial of service by causing decode to fail with unchecked runtime failures or traps rather than normal malformed-input decode errors.
Known impact:
- Availability impact only.
- No known confidentiality impact.
- No known integrity impact.
- No known code execution.
Attack requirements:
- The application decodes attacker-controlled protobuf bytes with Wire.
- No authentication is required if the decoding endpoint is reachable without authentication.
- A single short malformed protobuf payload is sufficient for the Kotlin byte-array fast path.
Most directly affected Kotlin entry points:
ProtoAdapter.decode(ByteArray)ProtoAdapter.decode(ByteString)
Adjacent Kotlin path hardened by this fix:
ProtoAdapter.decode(BufferedSource)- direct use of
ProtoReader
Affected Swift entry points:
- Swift
ProtoDecoderandProtoReaderAPIs when decoding attacker-controlledDataor buffers.
An arithmetic operation produces a value that exceeds the integer type's maximum, causing it to wrap to an unexpected small value. Typical impact: incorrect size calculations leading to heap overflows or logic errors.
CVE-2026-63126 has a CVSS score of 7.5 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (6.4.5, 7.0.0-alpha04); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
The fix centralizes checked cursor and pointer advancement.
Kotlin changes:
ByteArrayProtoReader32now validates constructor invariants forposandlimit.ByteArrayProtoReader32now uses shared helpers to:- reject negative lengths,
- compute checked limits,
- compute remaining bytes in the current logical limit,
- validate before
skip, - validate before string and bytes reads,
- validate before fixed32 and fixed64 reads.
ProtoReadernow mirrors the same logical-boundary model for:- length-delimited limit calculation,
- skipped length-delimited fields,
- varint reads,
- fixed32 reads,
- fixed64 reads,
- current-message remaining-byte calculations.
Swift changes:
ReadBuffernow computes checked end pointers only after confirmingcount >= 0andcount <= remaining.ReadBuffer.readVarint()verifies one byte remains before each byte dereference.ReadBuffer.readBuffer(count:),readData(count:),readFixed32(), andreadFixed64()compute the checked new pointer before reading and advancing.ProtoReader.beginMessage()validates nested message lengths before storing a message-end pointer.- Packed repeated decoding validates the packed field length before preallocation and before constructing the loop boundary.
ProtoDecoder.decodeSizeDelimited(_:from:)converts sizes withInt(exactly:)and verifies that the full message bytes exist before constructing a child buffer.
Fixed in PR #3635:
- https://github.com/square/wire/pull/3635
- Fix commit
25ebcabb9ab7f12d1d77af75ecbc51726fddc015
Frequently Asked Questions
- What is CVE-2026-63126? CVE-2026-63126 is a high-severity integer overflow or wraparound vulnerability in com.squareup.wire:wire-runtime (maven), affecting versions <= 6.4.4. It is fixed in 6.4.5, 7.0.0-alpha04. An arithmetic operation produces a value that exceeds the integer type's maximum, causing it to wrap to an unexpected small value.
- How severe is CVE-2026-63126? CVE-2026-63126 has a CVSS score of 7.5 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of com.squareup.wire:wire-runtime are affected by CVE-2026-63126? com.squareup.wire:wire-runtime (maven) versions <= 6.4.4 is affected.
- Is there a fix for CVE-2026-63126? Yes. CVE-2026-63126 is fixed in 6.4.5, 7.0.0-alpha04. Upgrade to this version or later.
- Is CVE-2026-63126 exploitable, and should I be worried? Whether CVE-2026-63126 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-63126 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-63126?
- Upgrade
com.squareup.wire:wire-runtimeto 6.4.5 or later - Upgrade
com.squareup.wire:wire-runtimeto 7.0.0-alpha04 or later
- Upgrade