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.
Recommended fix
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)
PoC verification
Evidence pack
findings/poc/DF-2793 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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_LOOKUPhas 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).
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_ucreddereference of a target proc.sys_rtprioandsys_lwp_rtprioapply it nowhere. sys_rtprio's RTP_LOOKUP additionally has no uid-ownership check before the copyout, and the RTP_SET ownership gate (:711-716) treatscr_uid == 0as 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_testablefix.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']
Confirmed kernel references
Detail
Exploit chain
root helper jails a child via jail(2) -> jailed root calls rtprio(RTP_LOOKUP,
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.
No comments yet.