A dictionary with a negative page number makes the reader walk off the book
CVE-2026-63209 affects the compress Go library's s2 package before v1.18.7. When an application calls s2.NewDict() with a malicious dictionary containing a uvarint-encoded repeat value larger than MaxInt64, the signed integer overflows to a negative number. Later, Dict.Encode() uses that negative value in unsafe.Pointer arithmetic and faults with SIGSEGV. It is a crash-only failure — no evidence of code execution, data corruption, or confidentiality impact.
The vendor severity of HIGH 7.5 is inflated by the CVSS vector AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. That vector assumes the application accepts remote attacker-controlled bytes and feeds them directly into s2.NewDict. In most real deployments, dictionaries are static, built-in, or generated from trusted content. The practical exposure is much narrower, so the vendor score does not match reality.
5 steps from start to impact.
Attacker reaches an endpoint that feeds attacker-controlled bytes to s2.NewDict
s2.NewDict. This is not a default behavior of the library; it requires application-specific plumbing such as accepting compression dictionaries from API headers, file uploads, or client parameters.- Application uses
github.com/klauspost/compress/s2version < 1.18.7 - Application accepts untrusted input and passes it as a dictionary to
s2.NewDict - No validation/sanitization on the dictionary content
- Most uses of
s2never expose dictionary input from the network - Dictionary content is typically fixed at compile time or derived from trusted data
- Typical compression endpoints accept the data to compress, not the dictionary
Craft a dictionary with an oversized uvarint repeat value
MaxInt64. When s2.NewDict parses it, the signed integer overflows and produces a negative repeat index instead of rejecting the input.- Attacker understands the s2 dictionary binary format
- Ability to send arbitrary bytes to the vulnerable API
- Requires knowledge of the internal dictionary format; not a simple malformed input
- No public exploit tooling has lowered attacker skill requirements
Trigger Dict.Encode with the poisoned dictionary
Dict.Encode() using the previously created malicious dictionary. The negative repeat index flows into unsafe.Pointer arithmetic.- Application automatically encodes data after creating the dictionary, or attacker can invoke the encode path
- If the application only creates the dictionary and never calls
Encode, the crash does not occur - Many code paths validate or discard malformed dictionaries before encoding
Process crashes with SIGSEGV
unsafe.Pointer arithmetic causes a segmentation fault, terminating the process. Because the impact is availability-only, the attacker gains no code execution or data access.- Compiled Go binary uses the vulnerable
s2version - Go runtime does not recover from the
unsafememory fault
- Only the current process crashes; unrelated services on the same host typically survive
- Automatic restart or process supervision can quickly resume service
Repeat crash requests to sustain denial of service
- Attacker can reach the vulnerable endpoint repeatedly
- Service automatically restarts or is otherwise reachable after each crash
- Rate limiting, connection limits, and load balancer health checks reduce the blast radius
- Each crash generates noise that security operations can observe
SIGSEGV crashes or process restart storms would flag the activity.github.com/klauspost/compress to v1.18.7 or later — This is the only permanent fix. Deploy within the noisgate remediation SLA of 365 days for MEDIUM severity. Update the Go module dependency, rebuild, and deploy the patched binaries.s2.NewDict — Reject dictionaries with uvarint repeat values greater than MaxInt64 or restrict dictionaries to an allow-list of static values. Deploy immediately, before the patch if needed.- Network WAF rules generally cannot identify malicious s2 dictionary bytes because they look like ordinary compressed data.
- Relying on Go memory safety does not help because the vulnerable code uses
unsafe.Pointer, bypassing Go's bounds checks. - DEP/ASLR and similar OS-level memory protections do not prevent the SIGSEGV crash.
The supporting signals.
| Affected component | github.com/klauspost/compress/s2 prior to v1.18.7 |
|---|---|
| Fixed version | v1.18.7 |
| CWE | CWE-190 Integer Overflow |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (HIGH 7.5) |
| EPSS score | 0.00572 (0.572% probability of exploitation in the next 30 days) |
| KEV status | Not listed by CISA Known Exploited Vulnerabilities |
| In-the-wild status | No active exploitation, public campaigns, or ransomware activity observed as of this assessment |
| Proof of concept | No public PoC located; exploitation requires custom-crafted dictionary bytes and an application-specific input path |
| Disclosure date | 2026-09-29 |
| Reporter/Org | Not disclosed in the NVD record |
Sources.
Why this verdict
- Remote exposure is rare: AV:N assumes network reachability, but the vulnerable path requires the app to accept untrusted bytes as an
s2.NewDictdictionary — not the normal compression use case. - Impact is availability-only: CVSS shows
C:N/I:N; the worst confirmed outcome is SIGSEGV, not code execution, privilege escalation, or data exfiltration. - Friction is high: Most applications initialize
s2dictionaries from trusted constants, and many never exposeDict.Encodeon attacker-influenced content. - Role multiplier: The bug is in a compression library, not in a domain controller, hypervisor, IdP, CA, PAM, backup, CI/CD registry, or network edge. A successful crash affects one process, not a domain, fleet, or supply chain.
Why not higher?
There is no evidence of active exploitation, no KEV listing, and EPSS is very low. The vulnerability does not provide integrity or confidentiality impact and does not map to a high-value-role outcome such as domain takeover or supply-chain pivot. The vendor score of 7.5 assumes the worst-case remote entry point that most deployments simply do not have.
Why not lower?
If an application does expose attacker-controlled dictionaries, repeated SIGSEGV can take down a critical service. The library is widely embedded across Go projects, and the fix is a simple version update, so it deserves a MEDIUM severity rather than LOW or IGNORE.
Crowdsourced verification payload.
Run this on a Linux/macOS build or CI host where the Go application source tree or module cache is available. No elevated privileges are required. Invoke with the path to the application's go.mod file: bash check_cve-2026-63209.sh /path/to/go.mod. The script parses the module version and outputs VULNERABLE, PATCHED, or UNKNOWN.
#!/usr/bin/env bash
set -euo pipefail
TARGET="github.com/klauspost/compress"
FIXED="1.18.7"
INPUT="${1:-}"
if [[ -z "$INPUT" ]]; then
echo "Usage: $0 /path/to/go.mod" >&2
exit 3
fi
if [[ ! -f "$INPUT" ]]; then
echo "UNKNOWN: file not found: $INPUT" >&2
exit 2
fi
# Extract module version from go.mod or go.sum
VERSION=$(awk -v mod="$TARGET" '
$1 == "require" && $2 == mod {print $3; exit}
$1 == mod {print $2; exit}
' "$INPUT" | sed 's/^v//')
if [[ -z "$VERSION" ]]; then
# Try go.sum
VERSION=$(grep "^${TARGET} " "$INPUT" 2>/dev/null | awk '{print $2}' | sed 's/^v//' | head -n1 || true)
fi
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN: $TARGET not found in $INPUT"
exit 2
fi
# Handle pseudo-versions like v1.18.6-0.2026...
VERSION_CLEAN=$(echo "$VERSION" | awk -F- '{print $1}')
compare_versions() {
local a="$1" b="$2"
if [[ "$a" == "$b" ]]; then echo 0; return; fi
local IFS=.
local i a_parts b_parts
read -r -a a_parts <<< "$a"
read -r -a b_parts <<< "$b"
for i in 0 1 2; do
local av="${a_parts[$i]:-0}"
local bv="${b_parts[$i]:-0}"
if (( av > bv )); then echo 1; return; fi
if (( av < bv )); then echo -1; return; fi
done
echo 0
}
CMP=$(compare_versions "$VERSION_CLEAN" "$FIXED")
if [[ "$CMP" -lt 0 ]]; then
echo "VULNERABLE: $TARGET version $VERSION_CLEAN < $FIXED"
exit 1
else
echo "PATCHED: $TARGET version $VERSION_CLEAN >= $FIXED"
exit 0
fi