# 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:

```c
/* 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:

```c
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
