β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2656

hammer2 zlib port drops zcalloc's zero-initialization: deflate window/state kmalloc'd unzeroed, leaving stale-heap reads whose output-independence rests on subtle invariants

Field Value
ID DF-2656
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:N
CWE CWE-908 Use of Uninitialized Resource
File sys/vfs/hammer2/zlib/hammer2_zlib_deflate.c
Lines 239, 254-256, 262 (inflate state :196)
Area vfs
Confidence certain
Discovered 2026-08-29
Pass 2 (GLM 5.3 second pass)
Bucket hammer2
Reported pending
Known CVE none
CVE match novel

Summary

Stock zlib 1.2.8 allocates deflate's window/prev/head/overlay via zcalloc (which returns zeroed memory). The port replaced these with kmalloc(..., M_INTWAIT) β€” none zeroed. Every algorithm-read field was verified still initialized before use EXCEPT the deflate sliding window beyond the data written so far: longest_match scans up to strstart+MAX_MATCH ignoring lookahead (deflate.c:641-757; the code itself documents the uninitialized accesses at :669-676) and fill_window's hash re-init reads window[str..str+MIN_MATCH-1] (:883-901, bounded in-bounds of written data). No leak into output is possible today: match length is capped to lookahead at return (:743-758) and match_start+length < strstart+lookahead bounds every emitted (length,distance) pair to written bytes, so decompressed output always equals the input. Residual risk: compression CHOICES (hence comp_size and on-disk compressed bytes) depend on stale heap contents β€” a nondeterminism surface with no observable channel found (physical compressed bytes, dedup XXH64 hashes, and failed-decompress reads are not exposed to unprivileged users).

Threat model & preconditions

Defense-in-depth only. The concern is fragility: any future edit that lets a match/literal reference window data beyond strstart+lookahead instantly becomes a cross-user kernel-heap disclosure into user-readable file contents, because the structural zeroing upstream zlib relies on is absent. Distinct from DF-0266 (inflate-side window).

Restore stock zcalloc semantics β€” add | M_ZERO to the five kmalloc sites (deflate.c:239,254-256,262; optionally the inflate state at inflate.c:196, though every inflate field read is explicitly reset today).

References

  • DF-0266 (inflate-side window leak), DF-2655 (error-path behavior surfaced in the same batch audit)

Timeline

  • 2026-08-29 Discovered during the pass-2 zlib batch audit (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2656 Β· 8 files
FileTypeDescriptionSize
VERDICT.md β€” 2.0 KB ↓ raw
README.md β€” 250 B ↓ raw
build.sh β€” 85 B view raw
run.sh β€” 49 B view raw
env.txt β€” 314 B view raw
manifest.json β€” 692 B view raw
verdict.json β€” 2.2 KB view raw
fix.diff β€” 1.7 KB view raw

DF-2656 evidence pack

  • finding: sys/vfs/hammer2/zlib/hammer2_zlib_deflate.c (239-262)
  • verdict: reproduced (certain) - see VERDICT.md
  • source-only confirmation (no runtime PoC)
  • reproduce: build.sh / run.sh
  • fix.diff: present, git-apply-able
VERDICT.md
↓ download raw

DF-2656 - Verification Verdict

Status: reproduced Impact: none (hardening) Confidence: certain

Mechanism

The port allocates the deflate state, window, prev, head and pending overlay with plain kmalloc(M_INTWAIT) - upstream zlib zcalloc() uses calloc semantics (ZEROED memory). Output independence therefore rests on runtime invariants rather than allocation guarantees: the port DOES keep the upstream fill_window high_water zeroing (lines 915-938 zmemzero window beyond high_water) and zmemzero(head) at init (line 194), which currently prevent stale-heap bytes from ever being read into the output window/match chain.

Verdict

source-confirmed (no runtime PoC; deterministic code trace of the cited path:line in the read-only sys/ tree). Code-certain divergence + invariant analysis: with the shipped guards, compressed output is independent of stale heap contents (the subtle invariant holds today); M_ZERO on the six allocations restores upstream calloc semantics as defense-in-depth so a future refactor of the high_water logic cannot turn stale kernel heap into compressed-file bytes. Compile-validated with the batch build.

Threat / reachability

Defense-in-depth on the hammer2 compression path: any future loss of the high_water zeroing invariant would leak freed kernel memory into compressed file contents.

Privilege escalation assessment

n/a (hardening)

Fix

Add M_ZERO to the six kmallocs (matches upstream zcalloc/calloc allocation semantics).

Kernel references

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable

fix.diff compile-validated in the batch combined kernel build (all 37 diffs of this batch applied to in-guest /usr/src; make -j6 nativekernel KERNCONF=X86_64_GENERIC, see batch build log referenced in VERDICT.md); no behavior A/B performed for this Info-tier item; behavior A/B intentionally skipped for Info tier

batch combined kernel build log (see findings/poc/_batch41 note in VERDICT.md)
↓ fix.diffper-fix-DF-2656

Confirmed kernel references

Detail

Exploit chain

no escalation path (Info-tier hardening; see VERDICT.md)

Evidence (decisive lines)

VERDICT.md mechanism/verdict sections; fix.diff

PoC changes

none (source-only confirmation for this Info-tier item)

Verified recommended fix

Add M_ZERO to the six kmallocs (matches upstream zcalloc/calloc allocation semantics).

Verdict

source-confirmed (no runtime PoC; deterministic code trace of the cited path:line in the read-only sys/ tree). Code-certain divergence + invariant analysis: with the shipped guards, compressed output is independent of stale heap contents (the subtle invariant holds today); M_ZERO on the six allocations restores upstream calloc semantics as defense-in-depth so a future refactor of the high_water logic cannot turn stale kernel heap into compressed-file bytes. Compile-validated with the batch build.