# DF-0035 — PoC

`sysctl_kern_msgbuf` (`sys/kern/subr_prf.c`) 3rd branch passes the wrong
length to `sysctl_handle_opaque`. This evidence pack proves the bug is a
real kernel OOB read, AND clarifies that the unprivileged-reachability claim
in the finding is incorrect (the OOB window requires root to open).

## The bug

At `sys/kern/subr_prf.c:1183`:

```c
} else if (n <= mbp->msg_size - rindex_modulo) {
    error = sysctl_handle_opaque(oidp,
                                 mbp->msg_ptr + rindex_modulo,
                                 n - rindex_modulo,   /* BUG: should be n */
                                 req);
}
```

`n` is the valid byte count; subtracting `rindex_modulo` (a buffer offset)
silently wraps to ~4 GiB (both are `u_int`) whenever `rindex_modulo > n`.
The resulting `copyout` then reads past `msg_ptr+msg_size` into adjacent
kernel memory.

## Reproduce

```
./build.sh
./run.sh unprivileged    # as any user; 2M poll of kern.msgbuf.  Expected: NO OOB
                         # (the bug is unreachable in normal operation).
                         # Exit 2 == "no leak observed" (the honest result).

# DECISIVE (root only; PANICS the kernel; use a disposable guest):
ssh dfbsd
cd /root/poc/DF-0035 && ./run.sh decisive
# -> kernel panic in std_copyout reading past msgbuf (proof of OOB read)
# -> reset the guest with `dfbsd-qemu/vm.sh reset` afterwards.
```

## What you'll see

- **Unprivileged path:** no leak. In normal operation `msg_bufr` tracks
  `msg_bufx - msg_size + 2048`, which forces branch 3 to fire only at
  `xindex_modulo==0` with `rindex_modulo=2048`. The buggy
  `n - rindex_modulo` then equals `msg_size - 4096` — a 2048-byte under-read,
  no OOB. Confirmed empirically: 2,000,000 sysctl reads as maxx, 0 hits.

- **Decisive path (kvm_write-forced geometry):** the kernel panics in
  `std_copyout` because the underflowed length (~4 GiB, clipped to oldlen)
  drives a `copyout` that walks off the end of `msg_ptr`'s mapped pages into
  adjacent unmapped kernel memory. Trap 0xc (page fault), rip inside
  `std_copyout+0x15a`. Reproduced twice; see `panic.txt`. Had the adjacent
  memory been mapped, the same OOB read would have leaked heap residue
  instead of crashing.

## Why unprivileged-only doesn't work

The OOB underflow condition (`rindex_modulo > msg_size/2` in branch 3)
requires `msg_bufr` to be "stale" at a value whose modulo exceeds
`msg_size/2`. That staleness is produced **only** by
`sysctl_kern_msgbuf_clear` (`subr_prf.c:1213-1218`, root-only — verified:
`sysctl kern.msgbuf_clear=1` as maxx → EPERM). Without root, the
steady-state geometry makes the bug a benign under-read. See `VERDICT.md`
for the full geometry trace.

## Fix

`fix.diff` — one-line change: `n - rindex_modulo` → `n`, matching branches 1
and 4. Matches the finding's `## Recommended fix` proposal exactly.
