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

Zone-end wrap check uses exact equality while unvalidated layer2->append_off / blockmap next_offset / hint can push next_offset past the zone boundary β€” allocations returned outside the requested zone on production kernels

Summary

hammer_blockmap_alloc/reserve terminate their offset walk only on exact equality 'next_offset == HAMMER_ZONE_ENCODE(zone+1,0)' (:162/:466), but the walk advances by unvalidated disk-derived values: 'next_offset += layer2->append_off - offset' (:256/:538, append_off a raw uint32 from media - up to ~4GB per iteration, multiple iterations accumulate) and 'next_offset += resv->append_off - offset' (:312/:573). A crafted layer2 append_off (forgeable CRC) - or a crafted vol0_blockmap[].next_offset / caller hint (the volume header CRC is never verified, DF-3042) - moves next_offset past the zone end without ever equaling it, carrying into the zone nibble (zone 8->9 etc.); the entry-side guards KKASSERT(HAMMER_ZONE_DECODE(blockmap->next_offset) == zone) (:137/:449) are INVARIANTS-only. Result: alloc returns offsets in a foreign zone (file data placed into META/BTREE big-blocks), breaking the zone-separation invariant the rest of HAMMER relies on; INVARIANTS kernels panic at one of the many KKASSERTs, production kernels silently store cross-zone data_offsets. All I/O still funnels through the buffer cache with the 52-bit phys mask - impact confined to the attacker's own crafted fs (zone confusion, metadata/data cross-placement), no kernel memory unsafety. Code-trace only (Low). Fix: production zone checks at entry + re-check after each advance + layer2->append_off <= HAMMER_BIGBLOCK_SIZE. [VERIFIED pass2: reproduced - crafted next_offset foreign zone panics at the exact cited 449 guard via first 512KB write; fix A/B same trigger completes with diagnostic no panic]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3077 Β· 10 files
FileTypeDescriptionSize
README.md β€” 328 B ↓ raw
VERDICT.md β€” 1.7 KB ↓ raw
craft_3077.c β€” 2.2 KB view raw
env.txt β€” 248 B view raw
fix.diff β€” 1.6 KB view raw
manifest.json β€” 679 B view raw
panic.txt β€” 14.2 KB view raw
phase_hammer3077.sh β€” 794 B view raw
run.log β€” 393 B view raw
verdict.json β€” 2.0 KB view raw

DF-3077 - Zone-end wrap check uses exact equality; crafted next_offset escapes the zone nibble

Severity Low | verdict: reproduced | impact: panic

Reproduce

Sources in this directory; build with cc -O2 -o <bin> <src> -pthread on the guest.

See VERDICT.md for the full narrative and run.log for the decisive output.

VERDICT.md
↓ download raw

DF-3077 verification

Kernel: DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Crafted-image reproduction using the DF-3040 forger technique: vol0_blockmap[zone 10 large-data].next_offset patched to HAMMER_ZONE_ENCODE(11, 0) (a foreign zone nibble; volume-header CRC is never verified per DF-3042). Read-only mount succeeds; the first large-data allocation (512KB dd) trips the cited entry guard exactly: panic assertion "HAMMER_ZONE_DECODE(blockmap->next_offset) == zone" failed in hammer_blockmap_reserve at hammer_blockmap.c:449, via hammer_ip_add_bulk/hammer_vop_strategy. On production (non-INVARIANTS) kernels the same crafted state would allocate cross-zone silently, as filed. Preconditions: root mount of crafted image + a >=16K write; local DoS / fs-internal zone confusion only.

Kernel references

Fix

See fix.diff.

Fix validation (A/B on rebuilt kernel #0 Sun Oct 25 06:10:12 UTC 2026)

fix_status: fixed - A/B validated: identical crafted image + 512KB write on the fix kernel completes with console diagnostic HAMMER(t77) blockmap zone mismatch for zone 10 and no panic (baseline panic.txt)

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.diffper-fix-DF-3077

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md + run.log + panic.txt + fix.diff + craft_3077.c (A/B: SURVIVED_3077_AB with zone-mismatch diagnostic)

PoC changes

adjusted from finding sketch to guest syscall/layout reality

Verified recommended fix

fix.diff adds a production zone check with early EINVAL return at both blockmap entry sites and turns the wrap equality checks into >=; A/B validated on the fixed kernel.

Verdict

Crafted-image reproduction using the DF-3040 forger technique: vol0_blockmap[zone 10 large-data].next_offset patched to HAMMER_ZONE_ENCODE(11, 0) (a foreign zone nibble; volume-header CRC is never verified per DF-3042). Read-only mount succeeds; the first large-data allocation (512KB dd) trips the cited entry guard exactly: panic assertion "HAMMER_ZONE_DECODE(blockmap->next_offset) == zone" failed in hammer_blockmap_reserve at hammer_blockmap.c:449, via hammer_ip_add_bulk/hammer_vop_strategy. On production (non-INVARIANTS) kernels the same crafted state would allocate cross-zone silently, as filed. Preconditions: root mount of crafted image + a >=16K write; local DoS / fs-internal zone confusion only.