# iskorotkov/avro: Integer Overflow in Decoder

Avro decoder narrowed attacker-controlled int64 wire lengths to int before bounds checks, bypassing size caps and panicking on 32-bit and via cumulative-size wrap.

- Payout: Research
- Severity: high
- Program: iskorotkov/avro
- Found by: klajok
- Disclosed: 2026-05-18
- CVE: CVE-2026-46384
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-mc57-h6j3-3hmv
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-mc57-h6j3-3hmv
- Tags: area:oss domain:oss impact:dos kind:research platform:ghsa sev:high tech:go technique:incomplete-fix technique:source-review vuln:dos vuln:memory-corruption

**Target:** Avro decoder (reader.go, codec_array/map, ocf) in Go library
**Root cause:** Wire-format int64 lengths and block counts were cast to platform int before range validation, so truncation/wrap bypassed MaxByteSliceSize/MaxSliceAllocSize/MaxMapAllocSize; cumulative sums used overflow-prone size += int(l) and MinInt negation was unsafe.
**Steps:**
1. Craft a bytes/string length of `(1<<32) + N`; on 32-bit it narrows to N and slips past Config.MaxByteSliceSize.
2. Craft a union index of `1<<32`; on 32-bit it narrows to 0 and selects types[0] despite the upper-bound check.
3. Encode an array/map across blocks: block 1 of 3, block 2 of `math.MaxInt - 2` with MaxSliceAllocSize=13; `size += int(l)` wraps to MinInt, `MinInt > 13` is false, decoder proceeds.
4. Send a block header whose absolute value is `math.MinInt` to trigger unsafe negation panic.
5. Send an OCF block size negative on the wire (or > MaxInt32 on 32-bit) to panic in `make([]byte, size)`.
**How found:** Code review of decoder call sites converting int64 wire values to int before validation, noting the limit comparison sees the truncated value; plus arithmetic review of cumulative size sums and MinInt negation.
**Exploitation:** Attacker supplies an untrusted Avro stream to a decoder; crafted lengths/indices/blocks bypass allocation caps or trigger panics, yielding denial-of-service (and on 32-bit, wrong union branch selection producing semantically wrong values).
**Insight:** When a size cap is enforced, check whether narrowing/casting happens before the comparison — the limit must see the original int64; and prefer subtraction-safe `l > limit - size` over `size + l > limit` to avoid signed wrap bypassing the cap.

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

**Array multi-block cumulative size overflow test**

```
TestDecoder_ArrayMultiBlockExceedsMaxInt`: `MaxSliceAllocSize = 13`, block 1 of `3`, block 2 of `math.MaxInt − 2`. Wraps to `math.MinInt`, check passes, decoder proceeds.
```

**Byte/string length narrowing payload**

```
A `bytes` or `string` length of `(1<<32) + N` for small `N`, which narrows to `N` on 32-bit and bypasses `Config.MaxByteSliceSize`.
```

**Union index narrowing payload**

```
A union index of `1<<32`, which narrows to `0` on 32-bit and selects `types[0]` despite the upper-bound check.
```

