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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5535 Β· 12 files
FileTypeDescriptionSize
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 via sysctl debug.mddiag=<mode>
  • dumpchk.c β€” parses the savecore vmcore, checks bitmap bits for given physical addresses
  • run.log β€” baseline console + dumpchk output
  • fix.diff β€” the fix (index correction + panic-time hardening)
VERDICT.md
↓ download raw

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)

  1. KLD mddiag.ko prints live facts: VM_MIN_KERNEL_ADDRESS = 0xfffff80040000000, KPDPPHYS_KVA = 0xfffff80000000000 (=> VMINK = slot 1), KERNBASE slot 8190, pdp[8190]/pdp[8191] frames 0x22f0000/0x22f1000 both V=1, walk1 max i = 8190.
  2. sysctl debug.mddiag=maptest maps two freshly allocated (bitmap-CLEAR) pages: va1=0xffffffffd0000000 (slot 8191) and va2=0xffffffffa0000000 (slot 8190, control), then panics. Real minidump written ("Dumping 680 MB ... Dump complete").
  3. 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

fixed
baseline reproduced→ patch + rebuild →patched reproduced

Applied 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')
↓ fix.diffper-fix-DF-5535

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.