# DF-5100 — joyread() spins inside crit_enter() against WALL-CLOCK nanotime()

## What
`joyread()` enters a critical section (`crit_enter()`, `joy.c:190`) and
busy-loops reading the game port until **wall-clock** `nanotime()` passes
`start + timeout` (`joy.c:192-211`), then `crit_exit()` (`joy.c:213`).

`nanotime()` is realtime: it adds the global `basetime`, which
`settimeofday()`/NTP steps mutate (`sys/kern/kern_clock.c:1562-1587`,
specifically `tsp->tv_sec += bt->tv_sec` at `kern_clock.c:1581`).

## Defect mechanics
* **By design (1995 ISA driver):** each `read()` masks interrupts and
  preemption on its CPU for up to `timeout` microseconds — max 10000us via
  `JOY_SETTIMEOUT` (`joyioctl`, `joy.c:243`). That alone is a 10ms
  interrupt-masked spin per read.
* **The bug:** the loop deadline is in *wall* time. A backward clock step
  (root `settimeofday(2)`, or an NTP daemon step — ntpd runs as root and
  steps by default) while a reader is inside the loop moves `nanotime()`
  backward, while `end = start + timeout` stays fixed; `timespeccmp(&t,
  &end, <)` keeps the loop spinning until the wall clock *again* passes
  `end`. A backward step of N seconds holds that CPU in a critical section
  (interrupts masked, preemption blocked, IPIs deferred) for ~N extra
  seconds. Nothing in the loop re-reads `timeout`, and the only exits are
  the wall-clock comparison and both-axis completion.

## Security impact
Root-gated (`/dev/joy*` is 0600 root, `joy.c:133`): an unprivileged user
cannot trigger it, and root alone can already stall the machine. The
realistic scenario is root running a joystick reader while the time
daemon steps the clock — a latent local DoS / latency hazard. Filed as
Info hardening; the fix is to use monotonic time (`nanouptime()`,
`sys/sys/time.h:185`) for the measurement loop.

## Build / run (as executed on the audit guest)
```
cc -O2 -Wall -o joy_crit_loop joy_crit_loop.c     # -> BUILD_OK, no warnings
./joy_crit_loop
```
Expected on hardware with `/dev/joy0`: five `read N: ~10000 us` lines,
each an interrupt-masked spin; a concurrent `date -v-N`-style backward
`settimeofday()` extends individual reads by ~N seconds.
Observed on this guest: exit 2, `open /dev/joy0: No such file or directory
(errno=2)` — joy(4) never attaches (no ISA gameport; `kldload joy` alone
creates no node). See `run.log`, `env.txt`.

## Fix
Swap `nanotime()` → `nanouptime()` at `joy.c:193` and `joy.c:204`
(see `fix.diff`). Monotonic time is the correct base for a bounded
busy-wait deadline.
