/proc/<pid>/rlimit read has no per-op authorization: any local user reads any process's full rlimit table
Summary
procfs_dorlimit() serves /proc/<pid>/rlimit with zero credential checks, unlike every sibling op (regs/fpregs/dbregs/mem/ctl all CHECKIO+p_trespass per operation; status gates argv sections). The only barrier is lookup-time visibility, and security.ps_showallprocs defaults to 1 (verified) - uid 1001 read 209 bytes of init's (uid 0) soft/hard limit table. VERIFIED twice on stock + patched control. Same data Linux gates behind PTRACE_MODE_READ for /proc/pid/limits - unpriv user fingerprints root/security-daemon limit configuration (memlock/nofile/stack of privilege-separated services). Also empirically pinned DF-0935's boundary: DF-0935 is UNFIXED in tree but unreachable with default loader caps (kernel clamps the limits; maximized formatter output is exactly 431 bytes < psbuf[512], 81 headroom; overflow needs root-relaxed maxdsiz-class tunables - confirms pass-1's Low). Fix: mirror the sibling gate (CHECKIO+p_trespass).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3259 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| rlimit_authz.c | β | 3.0 KB | view raw | |
| build.sh | β | 91 B | view raw | |
| run.sh | β | 97 B | view raw | |
| run.log | β | 1.1 KB | view raw | |
| run.2.log | β | 99 B | view raw | |
| fix.diff | β | 354 B | view raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| env.txt | β | 122 B | view raw |
DF-3259 VERDICT β /proc/pid/rlimit missing per-op authorization
Status: REPRODUCED (authorization bypass / cross-uid info disclosure), Low severity
Root cause
sys/vfs/procfs/procfs_rlimit.c:55-110 β procfs_dorlimit() contains no
credential check of any kind. The only filter is lookup-time process
visibility: sys/vfs/procfs/procfs_vnops.c:809-811,833 hide other-uid pid
directories only when security.ps_showallprocs == 0; guest default is
security.ps_showallprocs: 1 (verified). Every comparable procfs op re-checks
CHECKIO(curp, p) || p_trespass(curp->p_ucred, p->p_ucred) per operation
(procfs_regs.c:60, procfs_fpregs.c:59, procfs_dbregs.c:62,
procfs_mem.c:189, procfs_ctl.c:126); rlimit is the outlier. This is the
DF-3224 per-op lesson applied in reverse: the gate is not merely open-time β
it is absent entirely.
Observation
uid=1001 (maxx) opened and read /proc/1/rlimit (init, uid 0): 209 bytes,
full soft/hard limit table of the root process (run.log, run.2.log β
reproduced twice, stable). Discloses limit-based fingerprinting of security
daemons (e.g. memlock/nofile of privilege-separated services).
Impact ceiling
Low: numeric configuration disclosure of arbitrary local processes. No memory-safety impact; no privilege escalation. (Linux gates the same file behind PTRACE_MODE_READ; the info is considered worth gating there.)
Fix validation
Not kernel-rebuilt for this finding (Low severity, authorization gate only β
the orchestrator's Phase V budget went to the DB-3258 fix build). fix.diff
adds the exact sibling gate at procfs_rlimit.c:63; pattern is used verbatim
at five neighboring call sites. fix_status: not_testable (no rebuild), with
the diff provided for the upstream PR.
Secondary result (DF-0935 boundary, empirical)
The same PoC maximizes all 12 rlimits to the highest values an unprivileged process can reach (hard-limit lowering to 9223372036854775806 is unprivileged β kern_plimit.c:294-303 β and the kernel then clamps data/stack/nofile/nproc/posixlocks to maxdsiz=34359738368, maxssiz=536870912, maxfilesperproc=32528, maxprocperuid=2033, maxposixlocksperuid=32528 β kern_plimit.c:316-379). Resulting formatter output: 431 bytes (predicted == actual), under psbuf[512] by 81 bytes. DF-0935 (unfixed in tree) therefore remains unreachable with default caps β confirming pass-1; overflow requires relaxing maxdsiz/maxssiz-type loader tunables (root), after which the reader must merely read a suitably-limited root process's rlimit.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableLow-severity authorization gate; kernel rebuild budget spent on the DF-3258 fix validation. fix.diff provided (sibling-pattern gate); not compiled into the test kernel.
[]
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: 'uid=1001 read /proc/1/rlimit: 209 bytes' + full root limit table run.2.log: second reproduction, identical 209 bytes run.log tail: 'predicted max formatter output: 431 bytes ... actual /proc/self/rlimit length: 431 bytes'
PoC changes
none β written fresh for this finding
Verified recommended fix
Add CHECKIO(curp,p) || p_trespass(curp->p_ucred, p->p_ucred) EPERM gate at the top of procfs_dorlimit
Verdict
procfs_dorlimit has no CHECKIO/p_trespass per-op gate (unique among procfs ops), and security.ps_showallprocs defaults to 1, so unprivileged uid=1001 read 209 bytes of init's (uid 0) full rlimit table from /proc/1/rlimit β reproduced twice on stock kernel and again on the patched (DF-3258-fix) kernel. Low-impact cross-uid configuration disclosure; sibling gate added in fix.diff (not kernel-rebuilt for this Low finding). Secondary empirical result: unpriv-maximized formatter output is exactly 431 bytes < psbuf[512], confirming DF-0935 is unreachable with default kernel caps (clamps: maxdsiz=32G, maxssiz=512M, nofile=32528, nproc=2033, posixlock=32528).
No comments yet.