minidump PDP-walk off-by-one: pages mapped in the last 1GB slot of kernel VA ([0xffffffffc0000000, KvaEnd)) are silently missing from every crash dump
Summary
Walk-1 indexes KPDPphys[] VM_MIN_KERNEL_ADDRESS-relative but DFly reserves slot 0 (KPDPPHYS_KVA) so VM_MIN_KERNEL_ADDRESS is slot 1 - the walk reads one slot low and never reaches pdp[8191]: the PD/PT/data pages mapped only in the last 1GB of kernel VA are silently absent from every minidump. Proven by mapping two bitmap-CLEAR pages (slot 8191 vs 8190 control), panicking, and parsing the savecore bitmap: slot-8191 page CLEAR/MISSING vs control SET; with the KPDPPHYS_KVA-relative fix both SET. Panic-path only: misleading forensics + weak anti-forensic effect. Walk-2 already indexes correctly, confirming the intended base.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5535 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 3.1 KB | β raw | |
| mddiag.c | β | 7.7 KB | view raw | |
| dumpchk.c | β | 2.6 KB | view raw | |
| build.sh | β | 92 B | view raw | |
| run.sh | β | 182 B | view raw | |
| run.log | β | 2.2 KB | view raw | |
| run.patched.log | β | 955 B | view raw | |
| env.txt | β | 625 B | view raw | |
| fix.diff | β | 1.6 KB | view raw | |
| manifest.json | β | 825 B | view raw | |
| verdict.json | β | 3.1 KB | view raw |
DF-5535 β minidump PDP-walk off-by-one: pages in the last KVA slot silently missing from every crash dump
Target
sys/platform/pc64/x86_64/minidump_machdep.c (pass 2)
Build
On the guest (root):
mkdir -p /root/mddiag && cp Makefile mddiag.c dumpchk.c /root/mddiag/ cd /root/mddiag && make obj >/dev/null 2>&1; make cc -O -o dumpchk dumpchk.c
Makefile:
KMOD= mddiag
SRCS= mddiag.c
.include
Run (baseline, unpatched kernel)
kldload /root/mddiag/mddiag.ko sysctl debug.debugger_on_panic=0 sysctl debug.mddiag=maptest # maps 2 fresh pages (slot 8191 + slot 8190 control), then panics # after the dump completes, force reset via QEMU monitor if wedged, wait for savecore /root/mddiag/dumpchk /var/crash/vmcore.N 0xaa603000 0xaae01000
Expected (baseline)
pa 0xaa603000 (mapped at va 0xffffffffd0000000, PDP slot 8191): CLEAR (MISSING from dump)
pa 0xaae01000 (mapped at va 0xffffffffa0000000, PDP slot 8190): SET (in dump)
Run (patched kernel, fix.diff applied)
Same as above; expected: BOTH pages SET (dump complete).
Files
mddiag.cβ KLD rig: report() (PDP layout facts), maptest(), booby(), panic trigger viasysctl debug.mddiag=<mode>dumpchk.cβ parses the savecore vmcore, checks bitmap bits for given physical addressesrun.logβ baseline console + dumpchk outputfix.diffβ the fix (index correction + panic-time hardening)
Verdict β DF-5535 (REPRODUCED)
Finding
minidumpsys() walk-1 (sys/platform/pc64/x86_64/minidump_machdep.c:226-298) computes its
index into the KPDPphys[] PDP array as
i = ((va - VM_MIN_KERNEL_ADDRESS) >> PDPSHIFT) & (NPML4EPG * NPDPEPG - 1); (:238, :252)
but the array is KPDPPHYS_KVA-based and DragonFly reserves its slot 0:
VM_MIN_KERNEL_ADDRESS = KVADDR(KPML4I, NKPML4E*NPDPEPG - NKPDPE, 0, 0) = PDP slot 1
(sys/platform/pc64/include/vmparam.h:116-118; NKPML4E=16, NKPDPE=8191). The correct index
for va is ((va - KPDPPHYS_KVA) >> PDPSHIFT) β exactly what the second walk in the
same file uses for its kpdp base (:385-390). Walk-1 therefore reads the PDP entry of the
1GB slot below va, and since the loop runs va < KvaEnd (inside slot 8191), i caps at
8190: pdp[8191] β the PD page covering [0xffffffffc0000000, KvaEnd), the last 1 GB of
kernel VA β is never walked. Any page mapped only in that slot (its PD page, its PT
pages, and all data pages) is never marked in vm_page_dump and is silently absent from
every minidump. (Leaf-PF and 2MB paths elsewhere are unaffected; sibling walk-2 dumps the
PDP array itself correctly, so savecore sees a present-but-pageless top slot.)
Reproduction (baseline, fresh guest, X86_64_GENERIC 6.5-DEV)
- KLD
mddiag.koprints live facts:VM_MIN_KERNEL_ADDRESS = 0xfffff80040000000,KPDPPHYS_KVA = 0xfffff80000000000(=> VMINK = slot 1),KERNBASEslot 8190,pdp[8190]/pdp[8191]frames 0x22f0000/0x22f1000 both V=1,walk1 max i = 8190. sysctl debug.mddiag=maptestmaps two freshly allocated (bitmap-CLEAR) pages:va1=0xffffffffd0000000(slot 8191) andva2=0xffffffffa0000000(slot 8190, control), then panics. Real minidump written ("Dumping 680 MB ... Dump complete").dumpchk /var/crash/vmcore.1: -pa 0xaa603000(slot 8191): CLEAR β missing from the dump -pa 0xaae01000(slot 8190): SET β present Identical allocation/mapping path; the only difference is the covering PDP slot.
Impact
Silent crash-dump incompleteness for the top 1 GB of kernel VA. On the small test guest that
slot holds only scaffolding today (512 empty PTs, census in run.log), but any system whose
kernel VA grows into [0xffffffffc0000000, VM_MAX) (large-memory/long-uptime machines,
buffer-cache and kvm allocations) loses those pages from every crash dump β misleading
post-mortems and (weakly) anti-forensic: attacker-controlled kernel state resident in that
slot would never appear in the saved core. No privilege boundary is crossed; runs only at
panic/dump time. Severity: Low (correctness/forensics).
Fix
fix.diff: index walk-1 off KPDPPHYS_KVA (both computations). Validated: with the patched
kernel the same maptest yields both pages SET and the dump still completes.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedApplied fix.diff in the guest /usr/src, rebuilt (nativekernel) and rebooted into the patched kernel; identical maptest now yields the slot-8191 page SET in the dump (baseline: CLEAR) with the slot-8190 control still SET and a normal 'Dump complete'.
run.patched.log (dumpchk output on patched-kernel vmcore: both pages SET; 'Dumping 644 MB ... Dump complete')
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: rig prints VMINK=0xfffff80040000000 vs KPDPPHYS_KVA=0xfffff80000000000 (slot 1), walk1 max i=8190; dumpchk of baseline vmcore: pa 0xaa603000 (slot 8191) CLEAR vs pa 0xaae01000 (slot 8190) SET. run.patched.log: with fix.diff both SET.
PoC changes
PoC authored fresh for this finding: KLD rig (mddiag.c) with report/maptest modes and a sysctl trigger; dumpchk.c parser for the minidump bitmap; no seed existed.
Verified recommended fix
Compute the walk-1 PDP index from KPDPPHYS_KVA (i = ((va - KPDPPHYS_KVA) >> PDPSHIFT) & (NPML4EPG*NPDPEPG-1)) at both :238 and :252, matching walk-2's kpdp base.
Verdict
minidumpsys() walk-1 indexes the KPDPPHYS_KVA-based KPDPphys[] array with a VM_MIN_KERNEL_ADDRESS-relative index; VM_MIN_KERNEL_ADDRESS sits at PDP slot 1 (slot 0 reserved), so the walk reads the slot below va and never reaches pdp[8191]. Proven on the live guest: a page mapped at va 0xffffffffd0000000 (slot 8191) is silently absent from the produced minidump while an identically-created page in slot 8190 is present (bitmap parse of the savecore'd vmcore). Impact is silent crash-dump incompleteness for the last 1GB of kernel VA (its PD page, PTs, and data pages) - a forensics/correctness defect, not a privilege boundary.
No comments yet.