# DF-3590 — `/dev/psm0` is world-writable (0666) and `psmioctl()` has no privilege check

## What

`psmattach()` creates the device node world read/write
(sys/dev/misc/psm/psm.c:2025):

```c
sc->cdev = make_dev(&psm_ops, unit, 0, 0, 0666, "psm%d", unit);
```

`psmioctl()` (psm.c:2629-2935) contains **not one** `priv_check()` /
capability check. Consequently any local user can:

1. **open the device** — `psmopen()` enables the PS/2 aux device and arms
   IRQ12 (psm.c:1211, 1243-1246): an unprivileged user toggles shared
   hardware interrupt state;
2. **reprogram the pointing device** — `MOUSE_SETMODE` changes sampling
   rate, resolution, acceleration and operation level, issuing real
   i8042/aux command sequences (psm.c:2720-2805, 2788-2795);
3. **force raw level** — `MOUSE_SETLEVEL` to `PSM_LEVEL_NATIVE` (2) breaks
   the userspace protocol contract for a running moused(8)/X (psm.c:2811-2816);
4. **transact with the controller** — `MOUSE_READSTATE`/`MOUSE_READDATA`
   send `PSMC_SEND_DEV_STATUS`/`PSMC_SEND_DEV_DATA` (psm.c:2831-2844);
5. **deny the device to everyone** — the driver allows a single open
   (`psm_cdev_open`, psm.c:2222-2224: `PSM_OPEN` → `EBUSY`), so an
   unprivileged process that holds the fd open forever excludes
   moused(8)/X/syscons from the mouse — **root gets EBUSY**;
6. **starve the console keyboard** — every mutating ioctl funnels through
   `block_mouse_data()` (psm.c:2539-2575), which disables the *keyboard*
   port and keyboard interrupt on the shared i8042 for the duration of
   the controller transaction. Measured on the guest: ~6.8 ms of
   keyboard-disabled time per ioctl; a tight unprivileged loop keeps the
   shared controller (and keyboard input) continuously disabled.

## Reproduced on the audit guest (uid 1001 `maxx`)

```
$ ./psm_ioctl_poc
uid=1001 opened /dev/psm0 (O_RDWR): OK -- device is now ENABLED (aux IRQ12 armed)
MOUSE_SETMODE(rate=20,res=LOW): OK (7.0 ms of i8042 transaction, kbd port disabled during it)
MOUSE_SETLEVEL(NATIVE=2): OK
MOUSE_READDATA: OK, device returned 3 bytes: 08 00 00
[after attacks ] level=2 rate=20 resolution=-2 accelfactor=0 packetsize=4
PERSISTED: unprivileged user changed mouse sampling rate to 20
50x MOUSE_SETMODE hammer: 340.5 ms total (6.81 ms per call of kbd-controller downtime)
== 56 ioctls issued as uid 1001, 0 failures ==

# while maxx holds the device open:
# root open(/dev/psm0): Device busy  <-- moused(8)/X would fail to start
```

Additionally the interrupt path was stress-exercised by injecting 1535+
QEMU PS/2 mouse events through the monitor socket while two unprivileged
reader threads held the device open (irq12 count +1598 in 25 s) — guest
stayed healthy (this was the hunt for a hardirq/lockmgr race; negative,
see VERDICT.md).

## Build / run (on the DragonFly guest)

```
cc -O2 -Wall -o psm_ioctl_poc psm_ioctl_poc.c
cc -O2 -Wall -o hold_probe  hold_probe.c
./psm_ioctl_poc            # phases 1-6
./psm_ioctl_poc hold &     # then run ./hold_probe as root: EBUSY
```

## Fix

See fix.diff — restrict the node to root (`0600`), matching how the rest
of the input stack (syscons, kbd) is only usable through privileged
daemons on multi-user systems. moused(8) and X run privileged on
DragonFly today, so the permission change costs nothing functionally;
alternatively add `priv_check(td, PRIV_DRIVER)` to the mutating ioctls if
read access for unprivileged clients must be retained.
