β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0181

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0181 Β· 10 files
FileTypeDescriptionSize
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
README.md readme human-facing summary
↓ download 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 β€” adds SYSCTL_XUNLOCK()+SYSCTL_SLOCK() on the EPERM return.
  • VERDICT.md β€” full narrative.
VERDICT.md verdict full narrative + before/after transcripts
↓ download raw

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).

Fix verification

fixed

validated

see evidence pack
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Fri Jul 17 21:29:31 UTC 2026

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.