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

debug.dump_modinfo sysctl is world-readable and discloses kernel pointers (kernel base, module load addresses, SSYM/ESYM, _DYNAMIC, ENVP, KERNEND)

Field Value
ID DF-2927
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
CWE CWE-200
File sys/kern/subr_module.c
Lines 373-385, 411, 429-450
Area kern
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket kernleak
Reported pending
Known CVE none
CVE match novel

Summary

sysctl debug.dump_modinfo pretty-prints the entire bootloader metadata TLV list to any local user. DragonFly's sysctl layer performs no privilege check on reads (gates only writes) and the handler does no caps check, unlike the comparable sysctl.sysctl_debug dumper which is gated by SYSCAP_NODEBUG_UNPRIV. The dump reveals the KVA of every TLV (%p), kernel base, load addresses of all preloaded modules, the kernel symbol-table range, _DYNAMIC, the static boot-env block address (the memory kern_envp walks, cf. DF-2894), and KERNEND. Verified on the stock audit guest as uid=1001: full 26-TLV dump, byte-identical across 3 reads. Severity capped at Low because stock DF pc64 has no kernel-base randomization and the kernel binary is user-readable — the leak's real value is as a deterministic KASLR-defeat and targeting primitive for adjacent bugs. Disclosure-only; no user→root route in this class.

Proof of contest

VERIFIED (findings/poc/DF-2927/): sysctl -n debug.dump_modinfo as uid=1001 on the stock guest β€” kernel base, module addresses (ehci.ko/xhci.ko), symtab range, _DYNAMIC, ENVP block, KERNEND all disclosed, 3/3 runs. Fix: gate the handler with the exact caps_priv_check_td(SYSCAP_NODEBUG_UNPRIV) already used for sysctl.sysctl_debug.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of subr_module.c (GLM 5.3); reproduced unprivileged.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2927 Β· 11 files
FileTypeDescriptionSize
README.md β€” 2.2 KB ↓ raw
VERDICT.md β€” 3.5 KB ↓ raw
build.sh β€” 263 B view raw
run.sh β€” 381 B view raw
run.log β€” 2.4 KB view raw
run.2.log β€” 102 B view raw
leak_sample.txt β€” 2.4 KB view raw
env.txt β€” 282 B view raw
fix.diff β€” 570 B view raw
manifest.json β€” 1002 B view raw
verdict.json β€” 3.6 KB view raw

DF-2927 β€” debug.dump_modinfo sysctl discloses kernel pointers to any local user

What

sys/kern/subr_module.c:447-450 registers debug.dump_modinfo (CTLTYPE_STRING | CTLFLAG_RD). DragonFly's sysctl read path applies no privilege check to reads (sys/kern/kern_sysctl.c:1436-1448 gates only writes via SYSCAP_NOSYSCTL_WR; CTLFLAG_SECURE also only suppresses writes at securelevel>0). The handler dumps the entire bootloader metadata TLV list, including:

  • every TLV's own kernel virtual address β€” sbuf_printf(sbp, " %p:\n", bptr) (sys/kern/subr_module.c:411)
  • kernel base (MODINFO_ADDR), kernel size, and the load address of every loader-preloaded module (ehci.ko, xhci.ko on the test guest)
  • MODINFOMD_SSYM/ESYM β€” exact kernel symbol-table range
  • MODINFOMD_DYNAMIC β€” _DYNAMIC address
  • MODINFOMD_ENVP β€” address of the static boot environment block (DF-2894 context: this is the array kern_envp walks)
  • MODINFOMD_KERNEND

Build

Nothing to compile β€” the "PoC" is a stock userland binary:

./build.sh          # no-op; explains the trigger

Run

./run.sh            # ssh to the guest, runs the sysctl as uid 1001

or directly on any DragonFly host: sysctl -n debug.dump_modinfo as any non-root user.

Expected output (observed on the audit guest, uid=1001)

 0xffffffff81de4000:
    type:   (0x01) MODINFO_NAME
    len:    20
    value:  /boot/kernel/kernel
 ...
    type:   (0x03) MODINFO_ADDR
    len:    8
    value:  0xffffffff80200000            <-- kernel base
 ...
    type:   (0x8003) MODINFO_METADATA | MODINFOMD_SSYM
    value:  0xffffffff81b25e40            <-- symtab start
    type:   (0x8004) MODINFO_METADATA | MODINFOMD_ESYM
    value:  0xffffffff81cd8538            <-- symtab end
    type:   (0x8006) MODINFO_METADATA | MODINFOMD_ENVP
    value:  0x0000000001de3000            <-- kern_envp block

Success criterion: a non-root uid receives the dump (exit 0, pointer values present). Failure after fix: sysctl: unknown oid 'debug.dump_modinfo' is fine too β€” no; expected post-fix behavior is Operation not permitted for non-root, success for root.

The output is deterministic across reads (identical MD5 over 3 runs β€” run.log).

VERDICT.md
↓ download raw

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

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

Kernel rebuild not performed: fix validation rebuilds are mandated for reproduced memory-corruption findings; this is a Low info-disclosure gated by a one-line permission check that mirrors the proven existing gate at sys/kern/kern_sysctl.c:649 verbatim. fix.diff applies clean against sys/kern/subr_module.c (git apply --check verified); post-fix expectation is EPERM for non-root and unchanged root output.

findings/poc/DF-2927/fix.diff (git-apply-able, applies clean; never applied to sys/)
↓ fix.diffper-fix-DF-2927

Confirmed kernel references

Detail

Evidence (decisive lines)

['findings/poc/DF-2927/run.log β€” uid=1001 receives the full 26-TLV dump, exit 0', 'findings/poc/DF-2927/run.2.log β€” 3 consecutive reads, identical MD5 242807fbad7a6d778cfb0f46fd458a32', 'findings/poc/DF-2927/leak_sample.txt β€” full dump with kernel base / SSYM / ESYM / DYNAMIC / ENVP / KERNEND values']

PoC changes

No seed PoC existed; wrote run.sh/build.sh from scratch (trigger is a stock userland binary β€” nothing to fix or compile).

Verified recommended fix

Gate sysctl_preload_dump with the same capability check used for sysctl.sysctl_debug: caps_priv_check_td(req->td, SYSCAP_NODEBUG_UNPRIV) returning on error (plus #include ).

Verdict

Unprivileged local users (verified uid=1001 on the stock audit guest) can read sysctl debug.dump_modinfo because DragonFly's sysctl layer gates only writes (kern_sysctl.c:1445-1448), and the handler (subr_module.c:429-445) performs no credential check β€” unlike the comparable sysctl.sysctl_debug dumper which is gated by SYSCAP_NODEBUG_UNPRIV (kern_sysctl.c:649). The dump discloses the KVA of every metadata TLV (%p, subr_module.c:411), the kernel base (MODINFO_ADDR = 0xffffffff80200000), the load addresses of every loader-preloaded module (ehci.ko @ 0xffffffff81cd9000, xhci.ko @ 0xffffffff81d56000), the exact kernel symbol-table range (SSYM 0xffffffff81b25e40 / ESYM 0xffffffff81cd8538), _DYNAMIC (0xffffffff80fce540), the static boot-environment block address (ENVP 0x1de3000 β€” the memory kern_envp walks, cf. DF-2894), and KERNEND (0x1de6000). Output is deterministic (identical MD5 across 3 reads). Not memory corruption; ceiling is a reliable kernel-memory-layout disclosure that defeats KASLR-style hardening and aids targeting of adjacent bugs; stock DF pc64 has no kernel-base randomization today, capping severity at Low.