# DF-3197 — devfs rule ioctls reachable from jail: host-wide devfs ruleset manipulation (jail escape)

## Where
- `sys/vfs/devfs/devfs_rules.c:419` `devfs_dev_ioctl()` — no `priv_check()`, no jail check; only gate is the 0600 root:wheel `/dev/devfs` node (`devfs_rules.c:470`), which jailed uid 0 passes.
- Rules are stored in the GLOBAL `devfs_rule_list` (`devfs_rules.c:67`); `DEVFS_RULE_APPLY`/`DEVFS_RULE_RESET` walk the GLOBAL `devfs_mnt_list` (`devfs_core.c:1912-1931`) — every devfs mount on the system, including the host's `/dev` and every other jail's.

## What a jailed root can do (all demonstrated on the guest)
1. Open `/dev/devfs` inside the jail (control node visible ⇒ any jail devfs not explicitly hiding it — this guest ships no `/etc/devfs.rules`, so a plain jail devfs exposes it; the same holds for custom rulesets that forget to hide `devfs`).
2. `DEVFS_RULE_ADD` a `SHOW` rule → `DEVFS_RULE_APPLY "*"` → un-hides nodes the HOST hid in the jail's devfs (defeats the jail's devfs ruleset).
3. `DEVFS_RULE_ADD` a `PERM` rule with `mntpoint "/dev"` → `DEVFS_RULE_APPLY "/dev"` → rewrites permissions on the HOST's `/dev` (demonstrated: `/dev/console` 0600 → 0666).
4. Same primitive on any other jail's mounts.

## Files
- `jail_show.c`  — PoC (host phase mounts jail devfs + hides `mem`/`zero`; jail phase un-hides + opens)
- `build.sh`, `run.sh`
- `run.log` — decisive output (`JAIL-ESCAPE: unhidden + OPENED /dev/zero from inside jail`, host `/dev/console` 0666)
- `fix.diff` — consolidated fix (jailed() gate + the other five findings)
- `verdict.json`, `manifest.json`

## Build / run
```
cc -static -O -o jail_show jail_show.c
./jail_show        # as root on the host; expect JAIL-ESCAPE lines
```

## Notes
- `/dev/mem` was un-hidden and stat-able from the jail; opening it returns EPERM
  only because memio(4) itself checks `jailed()` — a driver-level defense devfs
  does not provide. Any device whose driver lacks that check is fully
  reachable once shown (demonstrated with `/dev/zero`).
- The rule engine also never bumps `mnt_namecache_gen` on SHOW/HIDE
  (`devfs_core.c:277` bumps only on node link); stale namecache entries mask
  flag changes until the generation is bumped — the PoC links one pid-unique
  alias to flush it.
