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

sys_getpgid / sys_getsid lack cross-session visibility checks (unprivileged pgid/sid enumeration)

Field Value
ID DF-0037
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
CWE CWE-862 Missing Authorization
File sys/kern/kern_prot.c
Lines 106-131 (getpgid), 136-158 (getsid)
Area kern
Confidence likely
Discovered 2026-06-29
Reported pending

Summary

sys_getpgid and sys_getsid look up an arbitrary target pid via pfind() and return its process-group-id / session-id without verifying that the caller may inspect that process. POSIX.1-2017 specifies EPERM when the target is not in the caller's session. Any local user can thus enumerate the pgid and session-id of every process on the system (including those in other sessions and UIDs).

Root cause

sys/kern/kern_prot.c:

/* sys_getpgid :119-126 */
pt = pfind(uap->pid);
...
lwkt_gettoken_shared(&pt->p_token);
sysmsg->sysmsg_result = pt->p_pgrp->pg_id;   /* no p_trespass / PRISON_CHECK */
lwkt_reltoken(&pt->p_token);

/* sys_getsid :149-154 */
pt = pfind(uap->pid);
...
sysmsg->sysmsg_result = pt->p_session->s_sid; /* no visibility check */

Contrast sysctl kern.proc (kern_proc.c:1690,1768) which calls PRISON_CHECK before revealing process metadata.

Threat model & preconditions

  • Attacker position: any local unprivileged user.
  • Privileges gained or impact: information disclosure β€” O(process-count) bits per call (the pgid/session-id of every process, including other sessions/UIDs). Session/pgid-leader fingerprinting; same class as historical BSD get*sid info-leak CVEs. Not an escalation primitive.
  • Required config or capabilities: none; default kernel.
  • Reachability: getpgid(2)/getsid(2) with an arbitrary pid.

Proof of concept

PoC source: findings/poc/DF-0037/leak_pgid.c

Build & run (unprivileged)

cc -o leak_pgid findings/poc/DF-0037/leak_pgid.c
./leak_pgid

Expected output

The pgid/sid of every process on the system, with no EPERM for out-of-session targets.

Impact

Low-grade information disclosure / POSIX non-compliance. Rated Info (same class as DF-0015/DF-0025).

Apply a p_trespass/session check before returning (mirroring kern.proc's PRISON_CHECK):

--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -123,6 +123,11 @@
    if (error == 0) {
        lwkt_gettoken_shared(&pt->p_token);
+       if (pt->p_session != p->p_session &&
+           caps_priv_check(p->p_ucred, SYSCAP_NOPROC_TRESPASS)) {
+           error = EPERM;
+       } else {
            sysmsg->sysmsg_result = pt->p_pgrp->pg_id;
+       }
        lwkt_reltoken(&pt->p_token);
    }

(and the analogous check in sys_getsid at :153-154.)

References

Timeline

  • 2026-06-29 Discovered during automated file-by-file audit of sys/kern/kern_prot.c.
  • pending Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0037 Β· 13 files
FileTypeDescriptionSize
leak_pgid.c trigger-source system-wide pgid/sid enumeration PoC 890 B view raw
leak_xsess.c trigger-source definitive parent/setsid-child cross-session probe (added by runner) 2.3 KB view raw
build.sh build-script cc -o leak_pgid leak_pgid.c; cc -o leak_xsess leak_xsess.c 157 B view raw
run.sh run-script unprivileged run of both PoCs 627 B view raw
run.log run-log baseline #0 decisive run: 22 procs enumerated, errno=0 cross-session 1.2 KB view raw
fix_run.log run-log patched #1 decisive run: EPERM, 3 procs (own session only) 574 B view raw
fix_build_tail.txt build-log nativekernel build log tail, NK_DONE rc=0 4.6 KB view raw
fix.diff suggested-fix adds pt->p_session != p->p_session && SYSCAP_NOPROC_TRESPASS check to both syscalls 856 B view raw
env.txt environment uname, cc version, kern.version 295 B view raw
VERDICT.md verdict full narrative: mechanism, proof, fix, validation 4.2 KB ↓ raw
README.md readme PoC overview 725 B ↓ 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 PoC overview
↓ download raw

DF-0037 β€” PoC

leak_pgid.c β€” unprivileged enumeration of every process's pgid/session-id via getpgid/getsid (no cross-session EPERM).

The bug

sys_getpgid (kern_prot.c:106-131) and sys_getsid (:136-158) call pfind(arbitrary pid) and return pt->p_pgrp->pg_id / pt->p_session->s_sid with no p_trespass/PRISON_CHECK/session-equality check. POSIX mandates EPERM for out-of-session targets; DragonFly returns the value to any caller.

Build & run (unprivileged)

cc -o leak_pgid findings/poc/DF-0037/leak_pgid.c
./leak_pgid

Expected output (bug present)

pid      1  pgid      1  sid      1
pid   <sshd> ... 
...
N processes' pgid/sid enumerated as uid=1000 (no EPERM)
VERDICT.md verdict full narrative: mechanism, proof, fix, validation
↓ download raw

DF-0037 β€” sys_getpgid / sys_getsid cross-session info leak

Field Value
Verdict REPRODUCED on unpatched #0; FIXED on single-fix #1
Impact Info disclosure β€” pgid/sid of every process on the system (no EPERM)
Class CWE-862 Missing Authorization (POSIX non-compliance)
Severity Info
Confidence certain
Reproduced on DragonFly 6.5-DEVELOPMENT #0 (Thu Jul 2 06:02:54 UTC 2026)
Fixed on DragonFly 6.5-DEVELOPMENT #1 (Tue Jul 14 19:55:09 UTC 2026)
Kernel file sys/kern/kern_prot.c

Mechanism

sys_getpgid (sys/kern/kern_prot.c:106-131) and sys_getsid (sys/kern/kern_prot.c:136-158) look up an arbitrary pid via pfind() and return its process-group-id / session-id without any cross-session visibility check. The code is simply:

/* sys_getpgid, lines 123-127 */
if (error == 0) {
    lwkt_gettoken_shared(&pt->p_token);
    sysmsg->sysmsg_result = pt->p_pgrp->pg_id;   /* no p_trespass / session check */
    lwkt_reltoken(&pt->p_token);
}

/* sys_getsid, lines 153-154 */
if (error == 0)
    sysmsg->sysmsg_result = pt->p_session->s_sid; /* no visibility check */

POSIX.1-2017 (getpgid(2), getsid(2)) requires EPERM when the target process is not in the caller's session. Contrast sysctl kern.proc (sys/kern/kern_proc.c:1690,1718,1768) which applies PRISON_CHECK / p_trespass before revealing process metadata.

Proof

leak_pgid.c (system-wide enumeration)

Scans pid 1..100000 and prints every successful getpgid/getsid as the unprivileged maxx user (uid 1001).

Before fix (#0): enumerates 22 processes including init (pid 1), sshd, getty, and unrelated sessions β€” with no EPERM:

pid      1  pgid      1  sid      1
pid     68  pgid     68  sid     68
...
22 processes' pgid/sid enumerated as uid=1001 (no EPERM)

leak_xsess.c (definitive cross-session test)

Parent forks a child that calls setsid() to create a brand-new session, then the parent (in the original session) probes the child's pgid/sid.

Before fix (#0):

[parent] pid=859 pgid=859 sid=859 uid=1001
[parent] probing child pid=860 (DIFFERENT session):
  getpgid(860) = 860  errno=0 (Undefined error: 0)
  getsid(860)  = 860  errno=0 (Undefined error: 0)
BUG DF-0037 REPRODUCED: parent (sid=859) leaked child's pgid/sid from a DIFFERENT session with no EPERM.

Fix

findings/poc/DF-0037/fix.diff β€” adds a session-equality + capability check (mirroring the p_trespass pattern in sys/kern/kern_prot.c:1023) to both syscalls:

if (pt->p_session != p->p_session &&
    caps_priv_check(p->p_ucred, SYSCAP_NOPROC_TRESPASS)) {
    error = EPERM;
} else {
    sysmsg->sysmsg_result = pt->p_pgrp->pg_id;  /* or pt->p_session->s_sid */
}

This matches the finding markdown's ## Recommended fix proposal verbatim: same predicate, same capability, applied to both syscalls.

Validation (Phase 8)

Phase Kernel Behavior
Baseline #0 (unpatched) 6.5-DEVELOPMENT #0 leak_xsess β†’ errno=0, no EPERM; leak_pgid β†’ 22 processes enumerated
Single-fix #1 (built + installed) 6.5-DEVELOPMENT #1 (sha256 c1819cd1..., build ts 19:55:09) leak_xsess β†’ errno=EPERM for cross-session target ("FIXED"); leak_pgid β†’ 3 processes (own session only); deterministic over 3 re-runs

The fix closes the leak completely: only processes in the caller's own session are returned; out-of-session targets correctly return EPERM. The SYSCAP_NOPROC_TRESPASS capability preserves root's ability to inspect any process (same mechanism as p_trespass).

Files in this evidence pack

  • leak_pgid.c β€” minimal PoC: system-wide pgid/sid enumeration
  • leak_xsess.c β€” definitive PoC: parent vs setsid-child cross-session probe (added by runner)
  • build.sh / run.sh β€” exact build/run commands
  • run.log β€” decisive baseline run (full output of both PoCs)
  • fix_run.log β€” decisive patched-kernel run
  • fix_build_tail.txt β€” kernel build log (rc=0)
  • fix.diff β€” git-apply-able fix (validated to apply + build + close the bug)
  • env.txt β€” guest environment (uname, cc, kern.version)
  • manifest.json β€” artifact catalog

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: baseline cross-session errno=0; patched EPERM. 22 procs -> 3 procs.

BEFORE: getpgid=860 errno=0. AFTER: getpgid=-1 errno=EPERM.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Tue Jul 14 19:55:09 UTC 2026

Confirmed kernel references

Detail

Exploit chain

none -- Info-level metadata disclosure (pgid/sid enumeration). No corruption.

Evidence (decisive lines)

BEFORE: getpgid(860)=860 errno=0 cross-session; 22 procs enumerated. AFTER: EPERM; 3 procs (own session).

PoC changes

Added leak_xsess.c (definitive cross-session), fix.diff (session check + SYSCAP_NOPROC_TRESPASS), VERDICT.md, manifest.json.

Verified recommended fix

Gate result in sys_getpgid(:125) and sys_getsid(:154): if(pt->p_session!=p->p_session && caps_priv_check(p->p_ucred,SYSCAP_NOPROC_TRESPASS)) error=EPERM. Matches finding proposal. Full diff in findings/poc/DF-0037/fix.diff.

Verdict

REPRODUCED. sys_getpgid/sys_getsid kern_prot.c:106-158 call pfind(arbitrary pid) + return pgid/sid with no session-equality check. Violates POSIX EPERM for out-of-session. maxx enumerates 22 processes incl init/sshd. Cross-session parent reads child pgid/sid errno=0.