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

rtprio()/lwp_rtprio() omit PRISON_CHECK β€” jailed root can read any host process's realtime scheduling class and priority (cross-jail info leak)

Field Value
ID DF-2793
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N
CWE CWE-668 Exposure of Resource to Wrong Sphere
File sys/kern/kern_resource.c
Lines 582-618, 701-716 (the five siblings all check)
Area kern
Confidence certain
Discovered 2026-08-31
Pass 2 (GLM 5.3 second pass)
Bucket kernleak
Reported pending
Known CVE none
CVE match novel

Summary

sys_rtprio() and sys_lwp_rtprio() never apply PRISON_CHECK, unlike getpriority/setpriority/ioprio_get/ioprio_set in the same file. RTP_LOOKUP has no credential check at all before copyout, and the uid-ownership gate short-circuits on cr_uid==0 β€” which jailed root satisfies. A jailed root (or a jailed user whose uid collides with a host uid) can read the rtprio {type, prio} of arbitrary host / foreign-jail processes. Write-side stays contained (unpriv cross-pid RTP_SET blocked; jailed root lacks SYSCAP_NOSCHED).

Proof of contest

VERIFIED (findings/poc/DF-2793/jail_rtprio.c): a jail(2)-confined root child read the host parent's self-set {RTP_PRIO_NORMAL, prio=7} and init's rtprio across the boundary while getpriority on the same pid returned ESRCH β€” the inconsistency on the same pid is the proof. Fix (add PRISON_CHECK to both syscalls) in the pack.

See findings/poc/DF-2793/fix.diff.

Timeline

  • 2026-08-31 Discovered during pass-2 audit of kern_resource.c (GLM 5.3); cross-jail leak reproduced same run.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2793 Β· 11 files
FileTypeDescriptionSize
jail_rtprio.c β€” 4.5 KB view raw
build.sh β€” 67 B view raw
run.sh β€” 181 B view raw
run.log β€” 446 B view raw
run.2.log β€” 446 B view raw
env.txt β€” 253 B view raw
fix.diff β€” 687 B view raw
README.md β€” 2.1 KB ↓ raw
VERDICT.md β€” 2.7 KB ↓ raw
manifest.json β€” 945 B view raw
verdict.json β€” 3.2 KB view raw

DF-2793 β€” rtprio()/lwp_rtprio() lack PRISON_CHECK: cross-jail read of

host scheduling parameters

What

sys_rtprio() (kern_resource.c:690-759) and sys_lwp_rtprio() (:558-670) perform no prison check on any path, unlike every other priority syscall in this file (getpriority :104/:123, setpriority :208/:233/:277, ioprio_get :347/:366, ioprio_set :450/:475/:520), all of which gate on PRISON_CHECK(curtd->td_ucred, p->p_ucred) (sys/sys/proc.h:462 β€” same-prison-only).

Consequences:

  • RTP_LOOKUP has no credential check at all before the copyout, so any jailed user whose uid matches a host user can read that host process's realtime scheduling parameters.
  • The uid-ownership gate (:613-618, :711-716) short-circuits on cr_uid == 0, so jailed root can RTP_LOOKUP any process on the host β€” revealing which host processes run realtime/idle classes and their exact priorities.

(RTP_SET stays contained for unprivileged cross-jail callers: the caps_priv_check(SYSCAP_NOSCHED) branch denies setting other pids :620-625/:718-723, and jailed root lacks NOSCHED.)

Build / Run (root β€” it jails a child to demonstrate the boundary)

cc -O2 -o jail_rtprio jail_rtprio.c
./jail_rtprio

The parent (host root) sets its own rtprio to {RTP_PRIO_NORMAL, 7} and forks; the child jails itself via jail(2) (DF jail(2) auto-attaches, kern_jail.c:227) and probes the parent's pid from inside the jail.

Expected output

[jailed root] parent (host, pid 27363) rtprio probe:
  rtprio(RTP_LOOKUP, 27363)      = OK  type=1 prio=7   <== LEAK
  getpriority(PROCESS, 27363)    = No such process (prison-checked)
  setpriority(PROCESS, 27363, 10) = No such process (prison-checked)
  rtprio(RTP_LOOKUP, 1/init)  = OK  type=1 prio=0   <== LEAK

rtprio returns the host process's exact prio 7 across the jail boundary while getpriority on the same pid correctly returns ESRCH.

Fix

fix.diff β€” add PRISON_CHECK to both syscalls (ESRCH on mismatch), matching the rest of the file. Authored post-verification; kernel rebuild not performed for this Low finding (fix_status: not_testable).

VERDICT.md
↓ download raw

DF-2793 VERDICT

Finding: sys_rtprio() and sys_lwp_rtprio() omit PRISON_CHECK entirely (sys/kern/kern_resource.c:582-601 / 701-704), breaking the jail confinement that every other priority syscall in the same file enforces.

Status: REPRODUCED. Severity Low (cross-jail information leak).

Root cause

  • PRISON_CHECK(cr1, cr2) (sys/sys/proc.h:462) is DragonFly's jail visibility test: (!(cr1)->cr_prison || (cr1)->cr_prison == (cr2)->cr_prison) β€” a jailed cred must only see same-prison procs.
  • getpriority (:104, :123), setpriority (:208, :233, :277), ioprio_get (:347, :366), ioprio_set (:450, :475, :520) all apply it around every p->p_ucred dereference of a target proc. sys_rtprio and sys_lwp_rtprio apply it nowhere.
  • sys_rtprio's RTP_LOOKUP additionally has no uid-ownership check before the copyout, and the RTP_SET ownership gate (:711-716) treats cr_uid == 0 as privileged, which jailed root satisfies. Result: jailed root can enumerate/read rtprio of arbitrary host processes; a jailed user can read host processes whose euid matches their uid.
  • Write-side containment (verified by inspection): for non-NOSCHED callers the if (uap->pid) EPERM at :720-723 (and :622-625) blocks cross-pid RTP_SET; jailed root lacks SYSCAP_NOSCHED (kern_jail.c:857- 869 denies group-5 caps in jails). So the reachable defect is read-only: scheduling class + priority of foreign-jail processes.

Run

Guest dfbsd 6.5-DEVELOPMENT X86_64_GENERIC, as root:

  • Child jails itself (jail(2), which in DF creates AND attaches β€” kern_jail.c:227) with path /tmp/jail2793.
  • From inside the jail (still uid 0, cred->cr_prison = new sub-jail):
  • rtprio(RTP_LOOKUP, parent_pid) β†’ OK, type=1 prio=7 β€” the exact value the host parent had set for itself β†’ real data crossed the boundary.
  • getpriority(PRIO_PROCESS, parent_pid) β†’ ESRH (prison check present) β€” same file, same target, different syscall.
  • setpriority(...) β†’ ESRCH likewise.
  • rtprio(RTP_LOOKUP, 1) β†’ OK β€” init's rtprio readable from jail.
  • Reproduced on both kernel #0 and the DF-2791-patched kernel #1.

Impact ceiling

Cross-jail confidentiality leak of scheduling metadata (rtprio class and priority) for all host/foreign-jail processes readable by jailed root, or by any jailed user where uids collide (common: uid 1001 in a jail and on the host). Value is host-process metadata, not kernel memory. Low severity; requires jails to be deployed.

Fix validation

fix.diff adds PRISON_CHECK(curthread->td_ucred, p->p_ucred) β†’ ESRCH to both syscalls. Not rebuilt (Low, read-path only, mirrors five existing call sites in the same file) β€” fix_status: not_testable.

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

fix.diff authored (PRISON_CHECK in both syscalls, ESRCH on mismatch); dedicated kernel rebuild not performed for this Low read-path finding β€” pattern validated against five existing PRISON_CHECK call sites in the same file.

['fix.diff']
↓ fix.diffper-fix-DF-2793

Confirmed kernel references

Detail

Exploit chain

root helper jails a child via jail(2) -> jailed root calls rtprio(RTP_LOOKUP, ) -> kernel returns the target lwp's {type, prio} with no prison check -> operator learns host processes' realtime/idle scheduling classes. No path to uid0 (read-only, metadata only).

Evidence (decisive lines)

["run.log / run.2.log: 'rtprio(RTP_LOOKUP, 27363) = OK type=1 prio=7 <== LEAK' vs 'getpriority(PROCESS, 27363) = No such process (prison-checked)'", 'VERDICT.md: prison-check inventory of all six priority syscalls in the file']

PoC changes

Removed the redundant jail_attach(2) call (DF's jail(2) auto-attaches, kern_jail.c:227; explicit attach from an already-jailed proc returns EPERM), resolved the target pid inside the child (getppid before fork captures the grandparent shell), fflush before _exit, direct syscall numbers 166/338/471.

Verified recommended fix

Add PRISON_CHECK(curthread->td_ucred, p->p_ucred) -> ESRCH to sys_rtprio() and sys_lwp_rtprio(), matching getpriority/setpriority/ioprio_* in the same file (fix.diff).

Verdict

sys_rtprio() (kern_resource.c:690-759) and sys_lwp_rtprio() (:558-670) apply no PRISON_CHECK, unlike the five other priority-syscall paths in the same file, so a jailed process can read scheduling parameters of foreign-jail/host processes: RTP_LOOKUP has no credential gate at all before copyout, and the RTP_SET ownership gate short-circuits on cr_uid==0 (jailed root). Demonstrated on the guest: a jail(2)-confined root child read the host parent's exact self-set rtprio {RTP_PRIO_NORMAL, prio=7} and init's rtprio across the boundary, while getpriority/setpriority on the same pid correctly returned ESRCH. Write-side is contained (unpriv cross-pid RTP_SET blocked at :718-723; jailed root lacks SYSCAP_NOSCHED), so impact is a cross-jail metadata leak: Low.