sysctl_hostname leaks XLOCK on EPERM: jailed root deadlocks host sysctl subsystem
Summary
sysctl_hostname(:217-219) acquires SYSCTL_XLOCK (LK_EXCLUSIVE on ALL CPUs) for writes. Jail check at :224-226 returns EPERM WITHOUT calling SYSCTL_XUNLOCK(:234-237). Lock leaked permanently. Root in jail lacking PRISON_CAP_SYS_SET_HOSTNAME: sysctl -w kern.hostname=x -> host-wide sysctl deadlock. All sysctl reads/writes/kldload hang forever. Jail->host DoS.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0181 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df0181_trigger.c | trigger-source | single-process jail+clear-cap+trigger+probe driver | 4.7 KB | view raw |
| df0181_probe.c | trigger-source | standalone host-sysctl probe with 5s SIGALRM | 826 B | view raw |
| build.sh | build-script | cc -O2 -Wall -o df0181_trigger/df0181_probe | 295 B | view raw |
| run.sh | run-script | driver script (uses jailproc wrapper) | 417 B | view raw |
| VERDICT.md | verdict | full narrative + before/after transcripts | 2.9 KB | β raw |
| fix.diff | suggested-fix | add SYSCTL_XUNLOCK()+SYSCTL_SLOCK() on the EPERM return at kern_mib.c:226 | 696 B | view raw |
| README.md | readme | human-facing summary | 1.2 KB | β raw |
| env.txt | environment | guest uname, modules, HW-gate note | 190 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0181 β sysctl_hostname XLOCK leak (jail->host DoS)
Summary
sysctl_hostname (kern_mib.c:209) leaks SYSCTL_XLOCK (LK_EXCLUSIVE
on every CPU) when a jailed process without
PRISON_CAP_SYS_SET_HOSTNAME attempts sysctl -w kern.hostname=....
The EPERM return at kern_mib.c:226 skips the matching SYSCTL_XUNLOCK()
at :235, permanently deadlocking the host sysctl subsystem.
Build / Run
./build.sh # builds df0181_trigger and df0181_probe
./df0181_trigger # must be run as host root
Expected (bug present)
- Output:
helper still running -- XLOCK LEAKED (bug confirmed). - Guest becomes unresponsive (host sysctl deadlocked).
Expected (fixed kernel)
- Output:
helper exited rc=1 -> lock NOT leaked. - Host sysctl continues to work.
Threat model
Exploitant: jail root (a tenant in a hosted multi-tenant environment). Unprivileged users cannot create jails, but jail-root is standard. CVSS: PR:H.
Files
df0181_trigger.cβ single-process driver (jail + clear-cap + trigger + probe).df0181_probe.cβ standalone 5 s-alarm sysctl probe.fix.diffβ addsSYSCTL_XUNLOCK()+SYSCTL_SLOCK()on the EPERM return.VERDICT.mdβ full narrative.
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:
- Helper (forked, host root, not jailed) waits for the JID on a pipe, then probes host sysctl with a 5 s SIGALRM.
- Parent calls
jail(2)(struct jail_v0, single IPv4) β this attaches the parent to a new jail withPRISON_CAP_SYS_SET_HOSTNAMEset (kernel defaultprison_default_caps). - Parent writes its JID to the pipe.
- Helper calls
sysctlbyname("jail.<jid>.sys_set_hostname", ..., 0)to clear the cap from outside the jail. - Parent (now jailed, cap cleared) issues
sysctl(kern.hostname=evil)-> takes the EPERM path at :226 -> leaks SYSCTL_XLOCK. - 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).
Fix verification
fixedvalidated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
REPRODUCED (live). sysctl_hostname EPERM return leaks SYSCTL_XLOCK -> host-wide sysctl deadlock. Jail-root.
No comments yet.