Skip to content
Heist/ feed
Back to feed
HighFix available

compress zstd: Panic on two-block frames

Type: advisorySources: OSV

### Summary A crafted ~27 KB two-block zstd frame makes DecodeAll and the streaming decoder panic with an out-of-range slice index instead of returning a decode error. The same bytes decode fine with the reference zstd. Any program that decompresses untrusted zstd input can be crashed by it. Reproduced on v1.20.0 and v1.20.1. Released in https://github.com/klauspost/compress/releases/tag/v1.20.2 ### Details The panic is in the FSE decoder that `huff0.ReadTable` uses to read a block's Huffman-weight table. Three conditions have to hold at once, and the crafted frame arranges all three. **1. A count that doesn't fit is accepted.** `readNCount` permits a tableLog up to 15 ([`tablelogAbsoluteMax`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L9), checked at [`fse/decompress.go:54`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L54)), so a symbol's normalized count can reach 32768 (2^15). The count is stored in an `int16` at [`fse/decompress.go:126`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L126), where 32768 wraps to −32768. The validation only looks at the `int32` running totals (`remaining` and `gotTotal`), which still match, so the bad value passes. The crafted second block produces `norm = [0, -32768]`, `symbolLen = 2`. **2. The decode table is built empty, with no error.** In `buildDtable`, −32768 isn't the −1 that flags a low-probability symbol, so it skips that branch, and the spread loop [`for i :=

Products
github.com/klauspost/compress/zstd
Affected versions
Not specified
Fixed versions
1.20.2
CVEs
CVE-2026-116591
Confirmed fixed CVEs
Not specified
Updated in this feed
1 Nov 2026, 18:44 UTC · Revision 1

Source evidence

compress zstd: Panic on two-block frames

core-osv · Original source

Published 27 Oct 2026, 15:38 UTC

### Summary A crafted ~27 KB two-block zstd frame makes DecodeAll and the streaming decoder panic with an out-of-range slice index instead of returning a decode error. The same bytes decode fine with the reference zstd. Any program that decompresses untrusted zstd input can be crashed by it. Reproduced on v1.20.0 and v1.20.1. Released in https://github.com/klauspost/compress/releases/tag/v1.20.2 ### Details The panic is in the FSE decoder that `huff0.ReadTable` uses to read a block's Huffman-weight table. Three conditions have to hold at once, and the crafted frame arranges all three. **1. A count that doesn't fit is accepted.** `readNCount` permits a tableLog up to 15 ([`tablelogAbsoluteMax`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L9), checked at [`fse/decompress.go:54`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L54)), so a symbol's normalized count can reach 32768 (2^15). The count is stored in an `int16` at [`fse/decompress.go:126`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L126), where 32768 wraps to −32768. The validation only looks at the `int32` running totals (`remaining` and `gotTotal`), which still match, so the bad value passes. The crafted second block produces `norm = [0, -32768]`, `symbolLen = 2`. **2. The decode table is built empty, with no error.** In `buildDtable`, −32768 isn't the −1 that flags a low-probability symbol, so it skips that branch, and the spread loop [`for i :=

osv:GHSA-fpmh-frq8-7rj8

github.com/klauspost/compress/zstd

Fixed versions: 1.20.2

Complete source record
{
  "id": "c0a5210b8f9a2cdf8908143f8f330fbe3fca9d11d61c83b810018d1415102de9",
  "url": "https://osv.dev/vulnerability/GHSA-fpmh-frq8-7rj8",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "compress zstd: Panic on two-block frames",
    "summary": "### Summary A crafted ~27 KB two-block zstd frame makes DecodeAll and the streaming decoder panic with an out-of-range slice index instead of returning a decode error. The same bytes decode fine with the reference zstd. Any program that decompresses untrusted zstd input can be crashed by it. Reproduced on v1.20.0 and v1.20.1. Released in https://github.com/klauspost/compress/releases/tag/v1.20.2 ### Details The panic is in the FSE decoder that `huff0.ReadTable` uses to read a block's Huffman-weight table. Three conditions have to hold at once, and the crafted frame arranges all three. **1. A count that doesn't fit is accepted.** `readNCount` permits a tableLog up to 15 ([`tablelogAbsoluteMax`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L9), checked at [`fse/decompress.go:54`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L54)), so a symbol's normalized count can reach 32768 (2^15). The count is stored in an `int16` at [`fse/decompress.go:126`](https://github.com/klauspost/compress/blob/v1.20.0/fse/decompress.go#L126), where 32768 wraps to −32768. The validation only looks at the `int32` running totals (`remaining` and `gotTotal`), which still match, so the bad value passes. The crafted second block produces `norm = [0, -32768]`, `symbolLen = 2`. **2. The decode table is built empty, with no error.** In `buildDtable`, −32768 isn't the −1 that flags a low-probability symbol, so it skips that branch, and the spread loop [`for i :=",
    "evidence": "osv:GHSA-fpmh-frq8-7rj8",
    "products": [
      {
        "name": {
          "value": "github.com/klauspost/compress/zstd",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "1.20.2",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": []
      }
    ],
    "severity": "high",
    "structured": {
      "id": "GHSA-fpmh-frq8-7rj8",
      "aliases": [
        "CVE-2026-116591",
        "GHSA-fpmh-frq8-7rj8"
      ],
      "metrics": [
        {
          "type": "CVSS_V4",
          "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "SEMVER",
              "events": [
                {
                  "introduced": "0"
                },
                {
                  "fixed": "1.20.2"
                }
              ]
            }
          ],
          "package": {
            "name": "github.com/klauspost/compress/zstd",
            "purl": "pkg:golang/github.com/klauspost/compress/zstd",
            "ecosystem": "Go"
          },
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-fpmh-frq8-7rj8/GHSA-fpmh-frq8-7rj8.json",
            "last_known_affected_version_range": "<= 1.20.1"
          }
        }
      ],
      "modified": "2026-10-27T15:47:24.256588090Z",
      "provider": "osv",
      "references": [
        "https://github.com/klauspost/compress/security/advisories/GHSA-fpmh-frq8-7rj8",
        "https://nvd.nist.gov/vuln/detail/CVE-2026-116591",
        "https://github.com/klauspost/compress/commit/7f5d8528720c895a6106f201719f87eb030a565d",
        "https://github.com/klauspost/compress",
        "https://github.com/klauspost/compress/releases/tag/v1.20.2"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-190"
        ],
        "severity": "HIGH",
        "github_reviewed": true,
        "nvd_published_at": "2026-10-26T22:16:55Z",
        "github_reviewed_at": "2026-10-27T15:38:14Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-116591",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/database_specific/severity"
  },
  "source_id": "core-osv",
  "document_id": 462840,
  "published_at": "2026-10-27T15:38:14+00:00"
}