# DF-3137 VERDICT — REPRODUCED (kernel-heap OOB write, panic + silent-neighbor variants)

## What was run

Guest: DragonFly dfbsd 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026,
X86_64_GENERIC (stock INVARIANTS kernel), single-tenant QEMU/KVM.
Crafted HPFS image (`craft_img.py`), mounted RW on /dev/vn0 via direct
mount(2) (`mount_rw.c`; stock mount_hpfs forces RO — DF-0859 precedent),
mountpoint owned by uid 1001. Trigger executed as uid 1001 (`dfbsd-maxx`).

## Run 1 — adjacent-chunk silent corruption (FREEOFF=500)

`pwrite(fd, 32768 bytes, offset 512)` as uid 1001 returned 32768; guest
stayed alive (run.alsec.500.log). Analysis: the new alleaf
{al_off=1, al_len=0x41, al_lsn=0x200} was written at
`C.b_data + 12 + 500 = b_data[512..524)` — 12 fully attacker-chosen bytes
(al_len/al_lsn crafted via the bitmap free-run layout, al_off via
fn_size) starting exactly at the end of the 512-byte buffer-cache chunk.
No kernel object complained → silent corruption of the neighboring heap
chunk (another 512-byte buffer data area in the same kmalloc zone).

## Run 2 — panic (FREEOFF=0x1000)

Same trigger with the write aimed 4 KB past the 512-byte AlSec buffer:
the unprivileged pwrite never returned; the kernel took

    Fatal trap 12: page fault while in kernel mode
    fault virtual address = 0xfffff80065b4700c
    fault code = supervisor write data, page not present
    instruction pointer = hpfs_addextentr+0xed:  movl %edx,(%r12,%rax,1)

`hpfs_addextentr+0xed` is exactly the `AL_SET(AB_FREEALP(rabp), ...)`
store at sys/vfs/hpfs/hpfs_alsubr.c:643 (`AL_SET` writes al_off/al_len/
al_lsn with three movl stores; this one is the al_len store at
`abp + freeoff + 4`). The fault VA is in the kernel heap (kmem map) —
the write reached unmapped kernel VA 0xfffff80065b4700c = the buffer
chunk + 0x1000 + 12. Guest died at db> (panic.txt).

## Primitive characterization (uid0 assessment)

* 12-byte write, contents = three u32s the attacker chooses when crafting
  the image (al_off <= 0x7FFFFF via fn_size; al_len/al_lsn via bitmap
  free-run geometry), at a fully chosen 16-bit offset (freeoff) from the
  start of a kmalloc'd 512-byte buffer (AlSec variant) or from
  `&hpfsnode->h_fn.fn_ab` (fnode variant — writes past the ~850-byte
  kmalloc(1024-class) hpfsnode).
* Repeatable and MARCHING: each additional extent insert in the same
  addextent loop writes another 12 bytes at freeoff+=12 — a contiguous
  structured heap overwrite of up to ~60 KB per pwrite (freecnt-bounded).
* On this no-KASLR/no-SMAP guest the write is kernel-heap-relative; a
  reliable uid0 chain additionally needs the absolute address of the
  AlSec data chunk (deterministic on a fresh boot but not directly
  observable from userspace) and a same-zone target at +0..+64K
  (neighbors are other 512-byte buffer-data chunks — the DF-3138 run
  demonstrates such adjacency is real and observable). Cross-zone
  targets (e.g. a neighboring hpfsnode's h_uid/h_gid/h_mode = exactly 12
  bytes) require zone-adjacency grooming that was not demonstrated
  within budget — documented as the hard blocker for the uid0 chain;
  both silent-neighbor corruption and unmapped-VA panic are proven.

## Fix validation

`fix.diff` adds `hpfs_alblk_sane()` (packed-array invariant:
busycnt <= capacity, freeoff == sizeof(alblk_t)+busycnt*sz,
freecnt <= capacity-busycnt) enforced at fnode load (hpfs_vfsops.c) and
at every AlSec read in hpbmap/addextentr/truncatealblk, plus the two
missing brelse()s (DF-3140) and the al_len==0 hole fix (DF-3139).
Applied to the guest's /usr/src, `make nativekernel`, rebooted:
the identical FREEOFF=0x1000 trigger returns EINVAL from pwrite
("hpfs_addextentr: INSANE ALBLK" on console), no panic, guest stays up
(fix_run.log). Baseline reproduced / patched not reproduced.

## Verdict

REPRODUCED: unprivileged controlled-offset 12-byte kernel heap OOB write
with attacker-chosen contents; panic at +4 KB, silent neighbor
corruption at +0. Fix validated. Kernel refs:
sys/vfs/hpfs/hpfs_alsubr.c:427-456 (root insert), :610-653 (AlSec
insert), :278 (concat dest), hpfs.h:152-171 (macros),
sys/vfs/hpfs/hpfs_vfsops.c:529-544 (no validation at load).

## 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.
