# DF-3099 VERDICT

**Verdict: REPRODUCED (deterministic unkillable kernel livelock / local DoS),
with the OOB-write primitive honestly characterized as unmanifested on the
stock build.**

## What was run (stock INVARIANTS kernel #0, guest `6.5-DEVELOPMENT`)

1. `whang` — `pwrite(fd, 1 byte, offset 0)` as uid 1001 on the crafted
   image (`alleaf{0, 0x800000, 0x50}`, `fn_size=0x1000`), mounted RW via
   `mount_rw` on a uid-1001-owned mountpoint.
   * Trace: `hpfs_write` → `uio_offset(0)+resid(1) ≤ fn_size` → no extend →
     `hpfs_hpbmap(0)` matches alleaf[0] → `runl = 0x7FFFFF` →
     `(runl+1)*512 = 0x800000*512 = 2^32 ≡ 0 (int32)` →
     `towrite = szmin(1, umin(65536, 0)) = 0`, `xfersz = 0` →
     `off==0 && towrite==xfersz` → `getblk(devvp, bn, 0)` + `clrbuf` +
     `uiomove(b_data+0, 0, uio)` — **no progress** → `while (uio->uio_resid)`
     loops forever holding the vnode lock (hpfs_vnops.c:385-430).
   * Observed: `RUN_EXIT=124` (pwrite never returned); guest load rose to
     `11.99`; subsequent `ls /tmp/hpfsmp` hung; ssh went to
     `Connection timed out during banner exchange`; VM management paths
     starved.  The triggering thread never sleeps → unkillable.
   * Console: `whang_console.txt`.  Run log: `run.whang.log`.

2. `wprobe` — 13× `pwrite(1 byte, off ∈ {1..511})` (only lbn 0 triggers):
   all returned `-1 EFAULT`.
   * Trace: `towrite = szmin(off+1, 0) = 0`, `xfersz = 0`,
     `off != 0` → `bread(devvp, bn, 0)` → zero-byte buffer →
     `uiomove(b_data+off, (size_t)(0-off) ≈ 2^64, uio)` with
     `uio_rw=UIO_WRITE` → `copyin(user_byte, b_data+off, 1)`.
   * The EFAULT proves the kernel aimed a **controlled 1-byte copyin** at
     `b_data+off` of a **size-0 buffer** — an OOB-write destination.  On
     this kernel build the data kva of a size-0 getblk buffer is unmapped,
     so the access faults cleanly instead of corrupting (13/13
     deterministic).  The memory-safety defect is real
     (length/destination computed from an uninitialized-by-size buffer
     region), but **no heap corruption could be landed on the stock build**,
     so impact is recorded as `dos`, not `memcorrupt`.
   * Run log: `run.wprobe.2.log`.

3. `readpanic` (read-side sibling, negative result worth recording): the
   original hypothesis — `xfersz > MAXBSIZE` reaching
   `panic("getblk: size > MAXBSIZE")` from `hpfs_read` — is **impossible on
   DragonFly**: `min` is a `u_int` static inline (sys/sys/libkern.h:76), so
   the inner `min(64*1024, (runl+1)*DEV_BSIZE)` converts the wrapped
   (negative) product to a huge `u_int` that never wins; `toread` is always
   ≤ 65536 = MAXBSIZE, and `toread==0` breaks out (vnops.c:331).  Empirically
   `read(fd, buf, 1MiB)` on an `al_len=0x400000` extent returned
   `EINVAL` from a benign 64 KB device read past EOF (run.readpanic.2.log,
   first attempt with al_len=0x80100000 in run.readpanic.log).

## Preconditions (honest)

* Crafted HPFS image (mount by root — `vfs.usermount=0`).
* **RW mount required**: stock `mount_hpfs(8)` forces `MNT_RDONLY`
  (sbin/mount_hpfs/mount_hpfs.c:106); the pack's `mount_rw.c` helper mounts
  via `mount(2)` directly, mirroring the DF-0859 precedent.
* Trigger itself is fully unprivileged (uid 1001 `pwrite`), with file
  ownership handed over via the RW mount's uid.

## Impact ceiling

Unprivileged local DoS: permanent kernel livelock with vnode lock held
(system-wide degradation, mountpoint unresponsive, management starvation).
The `uiomove` size underflow additionally constitutes a latent
attacker-controlled OOB-write primitive that manifests only where a
zero-size buffer's `b_data` is mapped kva; on the audited build it is a
clean EFAULT.

## Fix validation

`fix.diff` (run-hint clamp in `hpfs_hpbmap` + `towrite==0` progress guard in
`hpfs_write`), applied to the guest's `/usr/src`, `make nativekernel`,
rebooted: `whang` returns immediately (`pwrite = 1`), `wprobe`/`wleak`
perform normal in-bounds writes (`pwrite = 512`), benign control image
reads unchanged.  No regression.  See `fix_run.log`.

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