# DF-2927 — VERDICT

**Status: reproduced** (3/3 runs, deterministic) · **Impact: leak** (kernel
pointer / memory-layout disclosure to unprivileged local users) ·
**Confidence: certain**

## What was tested

Whether an unprivileged local user can read `sysctl debug.dump_modinfo`
(`sys/kern/subr_module.c:447-450`), which pretty-prints the entire
bootloader-supplied metadata TLV list — including kernel virtual addresses.

## Result

On the stock audit guest (see `env.txt`), `uid=1001(maxx)`:

- `sysctl -n debug.dump_modinfo` exits 0 and returns the full 26-TLV dump
  (`run.log`, `leak_sample.txt`).
- Output is byte-identical across 3 consecutive reads (MD5
  `242807fbad7a6d778cfb0f46fd458a32`, `run.2.log`) — the leak is stable and
  trivially parseable.

## What is actually disclosed

From `preload_dump_internal` / `preload_modinfo_value`
(`sys/kern/subr_module.c:365-427`):

| Value | Line | Observed (guest) |
|---|---|---|
| Every TLV's own KVA (`%p`) | :411 | `0xffffffff81de4000` … |
| Kernel base (`MODINFO_ADDR`) | :376,:384 | `0xffffffff80200000` |
| Kernel image size (`MODINFO_SIZE`) | :373-374 | 28149048 |
| Load addr of each preloaded module (`ehci.ko`, `xhci.ko`) | :384 | `0xffffffff81cd9000`, `0xffffffff81d56000` |
| Kernel symtab range (`MODINFOMD_SSYM`/`ESYM`) | :377-378,:384 | `0xffffffff81b25e40` … `0xffffffff81cd8538` |
| `_DYNAMIC` (`MODINFOMD_DYNAMIC`) | :379,:384 | `0xffffffff80fce540` |
| Static env block (`MODINFOMD_ENVP`) | :381,:384 | `0x0000000001de3000` |
| `kernend` (`MODINFOMD_KERNEND`) | :380,:384 | `0x0000000001de6000` |

## Root cause

`sysctl_preload_dump` (`sys/kern/subr_module.c:429-445`) performs no
credential check. DragonFly's generic sysctl layer only gates **writes**
(`sys/kern/kern_sysctl.c:1445-1448`, `SYSCAP_NOSYSCTL_WR` on `req->newptr`);
reads of non-`CTLFLAG_SECURE` OIDs are unrestricted for local users, and the
`SYSCAP_NODEBUG_UNPRIV` gate that protects the comparable
`sysctl.sysctl_debug` tree dumper (`sys/kern/kern_sysctl.c:649`) is *not*
applied to OIDs under the `debug` top-level node.

## Why it matters / impact ceiling

- Discloses the **exact runtime KVA layout**: kernel base, module load
  addresses, symtab bounds — full reconnaissance for any KASLR or
  KVA-hardening scheme (stock DF pc64 currently has no kernel-base
  randomization, which caps practical severity at Low today).
- Reveals the location of the static boot-environment block (`ENVP`), the
  memory `kern_envp` walks (see DF-2894) — useful targeting data.
- Requires zero privileges, zero setup, works on default installs.

Not memory corruption; no escalation path by itself. Ceiling: reliable,
deterministic kernel-layout leak.

## Fix

`fix.diff` gates the handler with the same capability check used by
`sysctl.sysctl_debug` (`SYSCAP_NODEBUG_UNPRIV`). Post-fix expectation:
non-root gets `EPERM` ("Operation not permitted"), root unchanged.

Fix validation: **not_testable-in-run** — a kernel rebuild cycle for a
one-line permission gate on a Low finding was judged out of proportion for
this run (no memory-corruption reproduction, which is what mandates the
rebuild per contract). The change mirrors an existing, proven gate at
`sys/kern/kern_sysctl.c:649` verbatim.

## Files

- `run.log` — decisive unprivileged run (uid=1001, full dump), untrimmed
- `run.2.log` — determinism check (3× identical MD5)
- `leak_sample.txt` — full 26-TLV dump as captured from the guest
- `env.txt` — guest uname / securelevel / root cred context
- `fix.diff` — the one-line privilege gate
