# DF-3590 — VERDICT

**Status: reproduced. Impact: unprivileged local DoS (console input
stack). Confidence: certain.**

## What was run, as whom, on what

Guest: `DragonFly dfbsd 6.5-DEVELOPMENT #0 X86_64_GENERIC` (stock
INVARIANTS kernel), QEMU/KVM with emulated i8042 + PS/2 mouse;
`psm0: model IntelliMouse Explorer, device ID 4`; `/dev/psm0` mode
`crw-rw-rw-` root:wheel. PoC built and executed entirely as uid 1001
(`maxx`), no groups beyond its own.

## Reproduced observations (each mapped to code)

1. **Open by unprivileged user** → `psm_cdev_open` has no permission gate
   beyond the devfs mode 0666 (psm.c:2208-2239); `psmopen` → `doopen`
   enables the aux device and turns on IRQ12 (psm.c:1211,
   1243-1253). Observed: open O_RDWR succeeded.
2. **Device reprogramming persisted**: `MOUSE_SETMODE` with rate=20 →
   later `MOUSE_GETMODE` reports `rate=20` (kernel state written at
   psm.c:2797-2802; controller transaction via
   `set_mouse_sampling_rate`). Observed.
3. **Level forced to NATIVE** → `MOUSE_SETLEVEL` (psm.c:2811-2816),
   `level=2 packetsize=4` afterwards; a subsequent moused(8) would
   mis-parse the byte stream. Observed.
4. **Controller transaction** `MOUSE_READDATA` returned live device data
   (`08 00 00`). Observed.
5. **Exclusive-hold denial**: while uid 1001 held the fd open, **root**
   `open()` → `Device busy` (single-open enforcement psm.c:2222-2224).
   moused(8)/X/syscons cannot acquire the mouse until the unprivileged
   holder releases — a trivially-scriptable, persistent, unprivileged
   console-DoS. Observed (`hold_probe`).
6. **Shared-controller/keyboard downtime**: each mutating ioctl runs
   `block_mouse_data()` (psm.c:2539-2575) which sets
   `KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT` for the whole i8042
   transaction, then `unblock_mouse_data()` restores. Measured: 6.8-7.0
   ms per `MOUSE_SETMODE`; 50 back-to-back calls = 340 ms during which
   the keyboard port is反复 disabled. An unprivileged tight loop keeps
   the console keyboard effectively dead. Observed (timing).

## What was hunted and killed (negative results worth recording)

- **Memory safety on the ioctl paths**: every copyout source is a
  kernel-resident struct written under `sc->lock` (or constants);
  `MOUSE_READSTATE`/`MOUSE_READDATA` bounds `data->len` against the
  array size with an unsigned compare that also rejects negatives
  (psm.c:2834-2835). No OOB.
- **`psmintr` byte-append overflow**: `pb->ipacket[16]` can hold at most
  `mode.packetsize` (≤ 6, from the vendortype table psm.c:710-748)
  because appends only happen while `inputbytes < packetsize`
  (psm.c:3044); the `KKASSERT` at psm.c:3036 never fires for any
  packetsize the driver can legitimately have; `MOUSE_SETMODE` cannot
  write `sc->mode.packetsize` (psm.c:2797-2802 copies only
  rate/resolution/accelfactor/level).
- **Ring buffers**: `psmsoftintr` queue insertion (psm.c:5231-5241) and
  `psmread` wrap-copy (psm.c:2514-2533) are correctly bounded.
- **hardirq → `lockmgr` race** (psmintr calling `psmsoftintr` directly
  from IRQ12 context, psm.c:3157 → lockmgr at 4955 on the sleepable
  `sc->lock`): stress-tested with 1535+ monitor-injected PS/2 events
  while two reader threads held the device (irq count +1598/25 s): no
  panic, no wedge. Uncontended acquisition from hardirq evidently works
  on this kernel; the hazard remains architectural but is not
  demonstrably exploitable — not filed as a finding.
- **`MOUSE_SETLEVEL` unlocked int write** racing psmintr/psmsoftintr:
  aligned int, no tearing, no memory-safety consequence.

## Exploit chain

Not a memory-corruption bug — no escalation path. Chain is the finding
itself: unprivileged user opens, reprograms, and/or holds `/dev/psm0`,
breaking mouse and degrading keyboard input for the whole console until
administrator intervention.

## Fix validation

Baseline reproduced (above). Kernel-rebuild validation of the one-line
`make_dev` permission change was not performed (Low-severity permission
finding, not a memory-corruption class; fix semantics are devfs-level
and fully determined by the diff). fix.diff passes
`git apply --check`.

## Kernel references

- sys/dev/misc/psm/psm.c:2025 — `make_dev(... 0666 ...)`
- sys/dev/misc/psm/psm.c:2629-2935 — `psmioctl` (no priv_check anywhere)
- sys/dev/misc/psm/psm.c:2720-2805 — MOUSE_SETMODE reprograms device
- sys/dev/misc/psm/psm.c:2811-2816 — MOUSE_SETLEVEL unlocked level flip
- sys/dev/misc/psm/psm.c:2539-2575 — block_mouse_data disables keyboard
- sys/dev/misc/psm/psm.c:2222-2224 — single-open EBUSY denial

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