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)
PoC verification
Evidence pack
findings/poc/DF-2927 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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.koon the test guest) MODINFOMD_SSYM/ESYMβ exact kernel symbol-table rangeMODINFOMD_DYNAMICβ_DYNAMICaddressMODINFOMD_ENVPβ address of the static boot environment block (DF-2894 context: this is the arraykern_envpwalks)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).
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_modinfoexits 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 memorykern_envpwalks (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), untrimmedrun.2.logβ determinism check (3Γ identical MD5)leak_sample.txtβ full 26-TLV dump as captured from the guestenv.txtβ guest uname / securelevel / root cred contextfix.diffβ the one-line privilege gate
Fix verification
not_testableKernel 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/)
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.
No comments yet.