# DF-4298 — kbd_token ↔ kb_lock lock-order inversion (AB-BA)

**Status: skipped (Low, race-window deadlock; not practically demonstrable
reliably on the single guest — static protocol violation documented with
line-exact citations).**

## Static analysis

Two conflicting acquisition orders exist for `lwkt_token kbd_token` and the
per-keyboard `kb_lock` (lockmgr):

**Order A — kbd_token → kb_lock**

* `genkbdioctl` (sys/dev/misc/kbd/kbd.c:802) takes `kbd_token`, then calls
  `kbd_ioctl` (kbd.c:808) → `kbdsw.c:209-211` `KBD_LOCK(kbd)` →
  `lockmgr(kb_lock, LK_EXCLUSIVE)` → driver `ioctl`.
* `kbd_register` (kbd.c:191-246) holds `kbd_token` across
  `kbd_ioctl(mux, KBADDKBD, ...)` (kbd.c:221/238) → same `KBD_LOCK` on the mux.

**Order B — kb_lock → kbd_token**

* syscons `sc_ioctl` (sys/dev/misc/syscons/syscons.c:1630-1631) calls
  `kbd_ioctl(sc->kbd, cmd, data)` **without** `kbd_token` → `KBD_LOCK` taken
  first → driver ioctl (e.g. `atkbd_ioctl` default case, atkbd.c:1113) →
  `genkbd_commonioctl` (kbd.c:999) takes `kbd_token` while `kb_lock` is held.
* Same shape: `vkbd_ioctl` holds `VKBD_LOCK` (vkbd.c state lock) then calls
  `genkbd_commonioctl` → `kbd_token`.

## Deadlock shape

Thread A (root, ioctl on `/dev/kbdN`): holds `kbd_token`, blocks acquiring
`kb_lock[X]`.
Thread B (unprivileged, ioctl on `/dev/ttyv*`): holds `kb_lock[X]`, blocks
acquiring `kbd_token` in `genkbd_commonioctl`.

`lockmgr` sleeps while `lwkt_token` stays owned by the sleeping thread ⇒ both
threads wedge (and any other CPU spinning on `kbd_token` stalls). Triggering
requires precise contention between a cdev ioctl (or keyboard registration at
module load) and a console ioctl — hence Low/likely (certain as a protocol
violation, racy as an exploit).

## Recommended fix

Make `genkbd_commonioctl`/`genkbd_get_fkeystr` token use consistent: either

* have `sc_ioctl`/`vkbd_ioctl` paths take `kbd_token` before `kbd_ioctl`
  (making kbd_token strictly outermost), or
* drop `kbd_token` from `genkbdioctl` (the `kbdsw` dispatch is already
  serialized by `kb_lock` + registration is token-protected), i.e.:

```diff
--- a/sys/dev/misc/kbd/kbd.c
+++ b/sys/dev/misc/kbd/kbd.c
@@ -799,7 +799,6 @@ genkbdioctl(struct dev_ioctl_args *ap)
 	keyboard_t *kbd;
 	int error;
 
-	lwkt_gettoken(&kbd_token);
 	kbd = kbd_get_keyboard(KBD_INDEX(dev));
 	if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
-		lwkt_reltoken(&kbd_token);
 		return ENXIO;
 	}
```

(one of the two; full analysis needed upstream — the token protects the
`keyboard[]`/`kbdsw[]` arrays, `kbd_get_keyboard` already takes it internally
and returns a stable pointer while `kb_lock` is held by `kbd_ioctl`).
