# Compress/s2: Integer Overflow or Wraparound and Out-of-bounds Write in compress

Signed integer overflow in s2.NewDict lets a uvarint repeat value above MaxInt64 bypass validation, causing OOB unsafe.Pointer read and SIGSEGV in Dict.Encode.

- Payout: Research
- Severity: high
- Program: klauspost/compress
- Found by: celinke97
- Disclosed: 2026-10-20
- CVE: CVE-2026-63209
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-259r-337f-4rfw
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-259r-337f-4rfw
- Tags: area:oss domain:oss impact:dos kind:research platform:ghsa sev:high tech:go technique:source-review vuln:dos vuln:memory-corruption

**Target:** Go s2 dictionary decoder (s2.NewDict / Dict.Encode)
**Root cause:** NewDict converts a uvarint to int without checking against MaxInt64, so values above it wrap negative and pass the `d.repeat > len(dict)` check. The negative repeat later overflows index arithmetic in encodeBlockDictGo, producing a negative index passed to load32's unsafe.Pointer arithmetic.
**Steps:**
1. Build dictData: `binary.PutUvarint(buf, 12687765596934209639)` (> MaxInt64), append 16 zero bytes for minimum dict size.
2. Call `s2.NewDict(dictData)`; it returns non-nil because `int(r)` is negative and bypasses `d.repeat > len(dict)`.
3. Call `dict.Encode(nil, src)` with src of 64 bytes.
4. encodeBlockDictGo computes `repeat := len(dict.dict) - dict.repeat` overflowing positive, then `candidate := len(dict.dict) - repeat + s` underflows negative.
5. `load32(dict.dict, candidate)` dereferences a negative index via unsafe.Pointer, causing SIGSEGV.
**How found:** Code review of NewDict's uvarint-to-int conversion and the subsequent `d.repeat > len(dict)` validation, noticing a negative int always passes a positive-length comparison; then tracing the negative repeat into encodeBlockDictGo's index math and load32's unchecked unsafe.Pointer arithmetic.
**Exploitation:** Attacker supplies crafted S2 dictionary bytes to any service calling s2.NewDict on untrusted input, then triggers Dict.Encode on ≥32 bytes of data; the negative index dereference crashes the process.
**Insight:** When a length or index is parsed as unsigned then cast to signed int, check the validation comparison for sign-bypass: a negative value trivially passes `> len(x)`. Trace the tainted value into unsafe.Pointer/unsafe.Add index math, which has no bounds checks.

