# DF-1851 — Verification Verdict

## Verdict: REPRODUCED (source-confirmed + arithmetic-harness)

The 32-bit integer overflow in the size-sum check is confirmed at
`sys/dev/raid/hptmv/hptproc.c:292`. The harness reproduces the
wraparound and shows the resulting 4095-byte heap overflow.

## Mechanism

```c
// hptproc.c:292 (under #ifdef SUPPORT_IOCTL, which IS defined — global.h:59)
if (piop->nInBufferSize + piop->nOutBufferSize > PAGE_SIZE)  // 32-bit add
    return -EINVAL;
// :297
ke_area = kmalloc(piop->nInBufferSize + piop->nOutBufferSize, M_DEVBUF, M_NOWAIT);
// :304
copyin(piop->lpInBuffer, ke_area, piop->nInBufferSize);  // 4095-byte copyin
```

Both sizes are `DWORD`/`unsigned int` (hptintf.h:748,750) — the
addition wraps mod 2³². With `nInBufferSize=4095, nOutBufferSize=0xFFFFF001`,
the sum wraps to `0x100000001 mod 2³² = 1`, which is `<= PAGE_SIZE`, so
the check is bypassed. `kmalloc(1)` returns a tiny slab; `copyin` then
copies 4095 bytes into it — a 4079-byte heap overflow. The subsequent
`Kernel_DeviceIoControl(ke_area+nInBufferSize=ke_area+4095, ...)`
writes OOB, and `copyout` on success leaks kernel heap.

This matches `FreeBSD-SA-09:11.hptmv`.

## Harness evidence

```
DF-1851: hptproc.c:292 size check (32-bit unsigned add)
  nInBufferSize = 0x00000fff (4095)
  nOutBufferSize= 0xfffff001 (4294963201)
  32-bit sum    = 0x00000000 (0)
  sum > PAGE_SIZE (4096)? NO (passes — check bypassed)
  -> kmalloc(0) returns a 0-byte slab
  -> copyin(lpInBuffer, ke_area, 4095) overflows it by 4095 bytes
  This is the FreeBSD-SA-09:11.hptmv class: integer-overflow in size sum.
```

## Why no live trigger on this guest (as unprivileged)

The `hptmv.status` sysctl IS registered on this guest (`device hptmv` is
in X86_64_GENERIC, and the sysctl handler runs at module init regardless
of HW). However, writing to sysctls requires `SYSCAP_NOSYSCTL_WR`
(kern_sysctl.c:1446) — `maxx` gets `Operation not permitted`. The bug
is therefore **root-only** on this guest: a root write to
`hptmv.status` with the crafted `HPT_IOCTL_PARAM` would trigger the
overflow (and then panic in `Kernel_DeviceIoControl` since
`gIal_Adapter` is NULL without HW).

This is a valid Phase-6 hard blocker for `uid=0`: the write is reachable
only from an already-root context, so there is no privilege boundary to
cross. (Root→kernel is game-over by definition.)

## Exploit chain

Not applicable (root-only trigger — no unpriv→root boundary). The bug
is a real root→kernel heap-overflow primitive; on a host with actual
HPT RAID HW, `gIal_Adapter` is non-NULL so `Kernel_DeviceIoControl`
runs and the corruption is silently weaponizable rather than panicking.

## PoC changes

- Added `harness.c`: reproduces the 32-bit wrap arithmetic.
- Added `fix.diff`: per-field bounds + `size_t` cast + `M_ZERO` +
  copyin return check.

## Fix

`fix.diff` bounds each size individually before the sum, casts to
`size_t` before adding to prevent 32-bit wrap, adds `M_ZERO` to the
`kmalloc`, and checks the `copyin` return.

- BEFORE: harness shows sum wraps to 0, bypassing the check.
- AFTER: per-field bounds reject `nOutBufferSize > PAGE_SIZE` before any
  addition.
