# DF-0181 — sysctl_hostname XLOCK leak (jail -> host sysctl deadlock)

## Verdict: REPRODUCED (DoS). Fix VALIDATED.

## Mechanism
`sysctl_hostname` (sys/kern/kern_mib.c:209-239):
- For writes (`req->newptr`), it acquires `SYSCTL_XLOCK()` at :219.
  `SYSCTL_XLOCK` is `_sysctl_xlock()` (kern_sysctl.c:1642), which takes
  `LK_EXCLUSIVE` on **every CPU's** `gd_sysctllock`.
- If the calling process is jailed AND the jail lacks
  `PRISON_CAP_SYS_SET_HOSTNAME`, the check at :224-226 returns `EPERM`
  **without** calling the matching `SYSCTL_XUNLOCK()` at :235.
- After the leak, EVERY subsequent sysctl read/write on the host
  (any jail, any CPU) blocks forever in `lockmgr(LK_SHARED)` waiting
  for the leaked exclusive lock.  The kernel is deadlocked.

## Trigger
`df0181_trigger.c` is a single-process driver:

1. **Helper** (forked, host root, not jailed) waits for the JID on a
   pipe, then probes host sysctl with a 5 s SIGALRM.
2. **Parent** calls `jail(2)` (struct jail_v0, single IPv4) — this
   attaches the parent to a new jail with `PRISON_CAP_SYS_SET_HOSTNAME`
   set (kernel default `prison_default_caps`).
3. Parent writes its JID to the pipe.
4. Helper calls `sysctlbyname("jail.<jid>.sys_set_hostname", ..., 0)`
   to clear the cap from outside the jail.
5. Parent (now jailed, cap cleared) issues
   `sysctl(kern.hostname=evil)` -> takes the EPERM path at :226 ->
   **leaks SYSCTL_XLOCK**.
6. Helper probes host sysctl; if it blocks past 5 s, the XLOCK is
   confirmed leaked.

## Reproduction (unpatched #0)
```
DF-0181: baseline kern.osrelease=6.5-DEVELOPMENT
DF-0181[jail]: jail() jid=1 (parent now jailed)
DF-0181[helper]: cleared jail.1.sys_set_hostname (readback=0)
DF-0181[trigger]: sysctl -w kern.hostname=evil rc=-1 errno=1 (Operation not permitted)
DF-0181[helper]: probing host sysctl (5 s alarm)...
DF-0181: helper still running -- XLOCK LEAKED (bug confirmed)
```
After this point the guest is unresponsive (`vm.sh status` => down,
`ssh` banner-exchange timeout): the host sysctl subsystem is deadlocked.

## Threat model
The exploitant in the realistic threat model is **jail root** (a tenant
in a hosted multi-tenant environment).  Unprivileged users cannot
create jails, but jail-root is the standard tenant privilege level
(`PR:H` per the CVSS).  A jail configured without
`PRISON_CAP_SYS_SET_HOSTNAME` (the secure default for untrusted
tenants) deadlocks the entire host sysctl subsystem -> jail->host DoS.

## Fix (validated)
`fix.diff` adds the missing `SYSCTL_XUNLOCK()` + `SYSCTL_SLOCK()` on
the EPERM return path.  On the patched kernel (#1, sha256
51001dcbc3613c6c607020114c46d1ec16818075d82632ada387d097c7cc7693):
```
DF-0181[trigger]: sysctl -w kern.hostname=evil rc=-1 errno=1 (Operation not permitted)
DF-0181[helper]: probing host sysctl (5 s alarm)...
DF-0181: helper exited rc=1 -> lock NOT leaked
```
Host sysctl continues to work (`sysctl kern.hostname` => `dfbsd`).
