# DF-5841 VERDICT — not_reproduced (reachability-gated on this configuration)

## Bottom line

The buffer-sizing/copyout inconsistency in `mapped_ioctl()` is **certain at
the code level**: for `com` with `IOC_VOID|IOC_OUT` (or `IOC_VOID|IOC_IN|IOC_OUT`)
and `IOCPARM_LEN(com)` in (128, 8191], the kernel copies `size` bytes out of
the 128-byte on-stack `ubuf.stkbuf` (`sys/kern/sys_generic.c:674-682` vs
`:729-730`) — an up-to-8063-byte OOB **kernel stack read** to userland, with
only the first 8 bytes (the user's own pointer) initialized. It did **not
reproduce** on the audit guest because the copyout is gated on the underlying
`fo_ioctl()` returning 0, and none does for a crafted unknown command on the
default configuration (58,496-command sweep, 0 hits — see `run.log`).

## Why the primitive is real (path:line)

1. `sys_ioctl` (`sys_generic.c:537-543`) passes `uap->com` verbatim to
   `mapped_ioctl` with `map == NULL` — the user fully controls `com`.
2. `size = IOCPARM_LEN(com)` (`:668`) is attacker-chosen in [0, 8191]
   (`sys/sys/ioccom.h:38-46`); the `size > IOCPARM_MAX` check (`:669`) can
   never fire since `IOCPARM_LEN` is already masked to 13 bits — it does NOT
   constrain direction combinations.
3. The allocation (`:674`) is skipped whenever `IOC_VOID` is set, regardless
   of size → `data = ubuf.stkbuf` (128-byte stack buffer, `:572-576`).
4. The `IOC_VOID` branch (`:681-682`) initializes only the first
   `sizeof(caddr_t)` = 8 bytes; the `bzero` for OUT commands (`:691-696`) is
   in an `else if` chain that the VOID branch short-circuits.
5. On `fo_ioctl()` success, `copyout(data, uspc_data, size)` (`:729-730`)
   runs for any `IOC_OUT` command — reading `size` bytes from the 128-byte
   stack buffer. No capability/priv check anywhere on this path; `com` is raw.

## Why it did not reproduce here

Every unprivileged-openable `fo_ioctl` dispatches on exact command constants
and errors for the crafted values:

- `vn_ioctl`  (`sys/kern/vfs_vnops.c:983`): FIONREAD/FIOASYNC exact, else
  `VOP_IOCTL` → spec → `dev_dioctl` → driver switch → ENOTTY.
- `pipe_ioctl` (`sys/kern/sys_pipe.c:993`): exact switch, default ENOTTY.
- `soo_ioctl` (`sys/kern/sys_socket.c:140+`): exact switch; the
  `IOCGROUP=='i'/'r'` defaults go to `ifioctl`/`rtioctl`, also exact-constant.
- `kqueue_ioctl` (`sys/kern/kern_event.c:1660`): default ENOTTY.
- `ttioctl` (`sys/kern/tty.c:853`): exact TIOC* constants, default ENOTTY.

Sweep: 11 fd types (/dev/null, /dev/zero, /dev/urandom, /dev/tty, VREG,
VDIR, pipe rd/wr, udp, tcp, unix socket, kqueue) × groups 0..255 × nums
{0,127} × dir {VOID|OUT, VOID|IN|OUT} at len=291 → 58,496 ioctls, **0
returned 0** (`run.log`: "done, hits=0").

Guest modules: kernel + ehci.ko + xhci.ko only — no evdev (`IOCBASECMD`
dispatch, `sys/dev/misc/evdev/cdev.c:514`), no sound (`IOCGROUP=='M'/'X'`,
`sys/dev/sound/pcm/dsp.c:1112,1141`), which are the only masked-command
dispatchers in the tree. Even those mask only the length field
(`IOCBASECMD` keeps the direction bits), so they would not match a VOID|OUT
crafted command either.

## Impact ceiling if a permissive driver exists

~8 KB of adjacent kernel stack disclosed to an unprivileged caller
(kernel text/stack pointers, saved state). On the standard audit snapshot
(no KASLR) this is an info-leak primitive for exploit chains; not itself
privilege escalation. Class: CWE-125 OOB read / CWE-909 info leak.

## Verdict

status: **not_reproduced** (the overread never executed — no `fo_ioctl`
returned success), reproduced: 0, impact: none (this configuration),
confidence: certain (code defect), severity filed: Info (hardening /
defense-in-depth). The fix is one line of allocation logic and removes the
primitive entirely; see `fix.diff`.

Attempts: 1 (58k-command sweep). Guest left clean (no panic, no dirty state).

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