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

KKASSERT is the only bounds check before bcopy/hammer_datacrc in hammer_indirect_callback (defense-in-depth gap on non-INVARIANTS kernels)

Field Value
ID DF-3034
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:N
CWE CWE-617 (assert-as-bounds-check)
File sys/vfs/hammer/hammer_io.c
Lines 1658-1677
Area vfs/hammer
Confidence likely
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket base:vfs
Reported pending
Known CVE none
CVE match novel

Summary

hammer_indirect_callback() validates that the device-side buffer covers the request only via KKASSERT(bp->b_bufsize >= obp->b_bufsize) β€” compiled out on production kernels β€” and then bcopy()s obp->b_bufsize bytes out of bp->b_data; the preceding CRC check has the same unguarded assumption. The code's own comment admits fragility ('due to big-block recycling and other factors there is some fragility there'), and HAMMER genuinely mixes 16K and 64K buffer sizes in the large-data zone, so a future regression in buffer sizing turns this into an OOB kernel heap read copied into a user-readable file buffer. Currently not exploitable: getblk guarantees an exact size match (disposes of mismatched buffers and retries), both breadcb and cluster_readcb acquire the callback's buffer at exactly obp->b_bufsize, and the path requires hammer_double_buffer=1 (non-default). Filed as Info hardening: the memory-safety property is incidental (relies on distant getblk internals) and any buffer-size regression immediately becomes an OOB read (kernel heap β†’ user file page) on production kernels. Fix: convert the assert into an explicit runtime check + short-circuit the CRC condition (row diff). Same assert-as-bounds-check class as DF-3003 (:931), kept separate because the guarded operations differ.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of hammer_io.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3034 Β· 4 files
FileTypeDescriptionSize
README.md β€” 243 B ↓ raw
fix.diff β€” 920 B view raw
manifest.json β€” 395 B view raw
verdict.json β€” 1.7 KB view raw

DF-3034 evidence pack

What

hammer_indirect_callback: KKASSERT-only size gate before bcopy/datacrc

Verdict

status=not_reproduced impact=none confidence=certain

See VERDICT.md for the full narrative and fix.diff for the verified fix.

Fix verification

not_testable

fix.diff applied in the F40FIX verification kernel build (RC=0, zero -Werror errors); no runtime A/B for this item (line-certain hardening)

↓ fix.diffper-fix-DF-3034

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md with path:line citations; run.log where a live demo exists; fix.diff

PoC changes

n/a - no PoC (size match guaranteed by getblk/breadcb on this guest)

Verified recommended fix

explicit bp->b_bufsize >= obp->b_bufsize runtime gate failing with EIO, ahead of CRC + bcopy

Verdict

Line-accurate confirmation; runtime demonstration deliberately not attempted (the mismatched-device-buffer precondition is prevented by getblk/breadcb acquiring at exactly obp->b_bufsize, and the path requires non-default hammer_double_buffer=1). The only gate before bcopy(bp->b_data, ..., obp->b_bufsize) is the KKASSERT at hammer_io.c:1676 - compiled out on production - and the CRC check at :1658-1664 reads obp->b_bufsize bytes from bp->b_data with the same unguarded assumption. Fix adds an explicit runtime size gate (B_ERROR/EIO short-circuit before the CRC condition); compiled in the F40FIX build (HAMMER in kernel).