Skip to content

fix: reject invalid dictionaries and support aliased decode buffers - #12

Merged
ChillerDragon merged 1 commit into
teeworlds-go:masterfrom
jxsl13:codex/huffman-api-safety
Aug 20, 2026
Merged

ChillerDragon merged 1 commit into
teeworlds-go:masterfrom
jxsl13:codex/huffman-api-safety

Conversation

@jxsl13

@jxsl13 jxsl13 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Follow-up review of current master (d21ea85) after #11.

Findings fixed

Invalid dictionaries

Dictionary is an exported type, so callers can construct its zero value or pass nil to NewHuffmanDict, NewReaderDict, NewWriterDict, CompressDict, and DecompressDict.

  • A zero-value dictionary made Compress and Writer.Write report success while emitting no frame, causing silent data loss.
  • A nil dictionary caused panics in the codec paths.

All encode/decode entry points now reject nil or uninitialized dictionaries through the existing ErrHuffmanCompress / ErrHuffmanDecompress chains. Nil codec, Reader, and Writer receivers also return defined errors instead of panicking. A copied, constructor-built dictionary remains valid.

Overlapping DecompressTo buffers

DecompressTo has append-style semantics, but an output slice whose writable capacity overlapped data could overwrite compressed bytes before they were consumed. High-expansion inputs such as long zero runs reliably turned this into truncation errors or corrupted decoding.

The decoder now detects overlap between the writable destination range and compressed input and preserves the source only in that exceptional case. Separate reused buffers retain their zero-allocation path. The overlap helper only compares addresses, never converts integers back to pointers, and uses subtraction so the arithmetic is safe on 32-bit platforms.

Compatibility and security impact

  • Default Teeworlds/DDNet dictionary and wire format are unchanged; golden compatibility tests pass.
  • Normal Teeworlds packet handling already uses an initialized default dictionary. The overlap issue only applies to integrations that reuse the compressed backing array as output storage.
  • Malformed packet bounds, EOF handling, and the one-symbol-per-input-bit expansion limit are unchanged.

Verification

  • go test ./... -count=1
  • go test -race ./... -count=1
  • go test -gcflags=all=-d=checkptr=2 ./... -count=1
  • go vet ./...
  • staticcheck ./...
  • govulncheck ./...: no vulnerabilities found
  • 2,000 randomized custom frequency tables, including zero, sparse, maximal, and uniformly random profiles
  • Fuzzing on the final implementation: 321k malformed decoder executions and 1.08M valid round trips
  • Cross-builds: linux/386, linux/arm, linux/mips, linux/mipsle, windows/386, freebsd/386, linux/s390x, wasip1/wasm
  • Full suite executed successfully in an actual linux/386 container
  • Alternating benchmark comparison against upstream/master: no statistically significant individual changes, +0.37% full-suite geomean, identical allocations

@ChillerDragon
ChillerDragon merged commit f958552 into teeworlds-go:master Aug 20, 2026
1 check passed
@ChillerDragon

Copy link
Copy Markdown
Member

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants