# DF-0862 — hpfs_bminit integer overflow in hpm_dbnum (OOB read / panic)

**Verdict:** REPRODUCED (panic/DoS via crafted HPFS image). **Fix VALIDATED.**
**Severity:** Medium.  **Impact:** kernel panic at mount (local DoS).

## What the bug is
`su_btotal` (a `u_int32_t` read verbatim off the HPFS SuperBlock at mount and
never validated) feeds `(su_btotal + 0x3FFF) / 0x4000` in `hpfs_bminit`
(`sys/vfs/hpfs/hpfs_subr.c:109`), evaluated in **u32** arithmetic. For
`su_btotal` in `[0xFFFFC001, 0xFFFFFFFF]` the `+0x3FFF` wraps mod 2^32, so
`hpm_dbnum` becomes **0**; the two `kmalloc(hpm_dbnum * …)` calls then hit the
slab allocator's `kmalloc(0)` special case and return the sentinel
`ZERO_LENGTH_PTR == (void*)-8 == 0xFFFFFFFFFFFFFFF8` (kern_slaballoc.c:888/193).
The bitmap-bitcount loop at `:156`, bounded by the **un-wrapped** `su_btotal >> 5`
(up to ~134M iterations), then dereferences `hpm_bitmap[0]` at that non-canonical
address → immediate page fault on mount.

## Threat model / reachability
HPFS is `optional hpfs` (not in X86_64_GENERIC) but shipped as `/boot/kernel/hpfs.ko`.
An admin enables it with `kldload hpfs` (standard FS-enable action). The attacker
supplies a crafted image; the mounter (root, or an unprivileged user after
`vfs.usermount=1` + an owned memory disk) mounts it → kernel panic. Classic
filesystem-image-parsing memory-safety bug.

No unpriv→root escalation: the fault is a single OOB **read** of a sentinel that
panics on the first iteration; the write path (`bmmark`/`hpfs_bmdeinit`) is never
reached because mount dies first. Impact ceiling = local DoS / panic.

## Reproduce
```sh
./build.sh                       # cc -O2 -Wall -o craft_img craft_img.c
./craft_img crafted.img 0xFFFFC001
# as root (the victim mounting the attacker image):
kldload hpfs
DEV=$(vnconfig -c vn $(pwd)/crafted.img | grep -oE 'vn[0-9]+' | head -1)
mount -t hpfs -o ro /dev/$DEV /mnt/df0862   # UNPATCHED: Fatal trap 12, guest dies
```
Expected on the **unpatched** kernel (serial `boot.log`):
```
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0xfffffffffffffff8
Stopped at hpfs_bminit+0x230: movl (%rax),%edi
```
Expected on the **fixed** module: `mount_hpfs: /dev/vnN: Invalid argument` (EINVAL),
guest stays up, dmesg: `hpfs_mountfs: su_btotal 4294950913 exceeds device capacity 262144`.

## The fix (`fix.diff`)
1. **hpfs_vfsops.c** — validate `su_btotal` against the backing device via
   `DIOCGSECTORSIZE`+`DIOCGMEDIASIZE`; reject (EINVAL) if `su_btotal*secsize > mediasize`.
2. **hpfs_subr.c** — compute the band count in 64-bit
   (`((u_long)su_btotal + 0x3FFF) / 0x4000`; `hpm_dbnum` is already `u_long`) and
   reject `su_btotal == 0`. This defends the arithmetic itself even when the
   geometry ioctls are unavailable.

Validated: built the single-fix `hpfs.ko`, installed it, re-ran the **same** PoC —
panic is gone, mount returns EINVAL promptly and the guest stays up (deterministic
over 2 runs). See `VERDICT.md`, `fix_run.log`, `panic.txt`.
