DF-2829 / verdict.json
{ "finding_id": "DF-2829", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "zinit() kmallocs struct vm_zone without M_ZERO (vm_zone.c:453) and zinitna() never initializes z->znalloc (vm_zone.c:347-436; the field is only incremented at :773/:786). The world-readable sysctl vm.zone prints it (:848,:854-858) - verified readable as nobody. On the stock INVARIANTS guest, a heap-groomed KLD harness produced REQUESTS=4702111234474983745 (0x4141414141414141) for a zero-activity zone: the groomed 8-byte heap marker crossed verbatim to unprivileged userspace on the first attempt. Stock exposure: SWAPMETA (swap_pager.c:441, every kernel) and the netbt pools (bt_proto.c:139-149, device bluetooth); on a fresh boot the stale qword read 0 (early-boot fresh pages), so the realistic yield is boot-time heap residue; the structural uninitialized read is default-config present. Fixed by znalloc init + M_ZERO + bzero-before-zlist-insert: patched kernel #1 prints REQUESTS 00000000 for the identical run.", "exploit_chain": "unpriv user -> sysctl vm.zone -> REQUESTS column of zinit()-created zones = 8 bytes of stale kernel heap (decimal) per zone, incl. the transient pre-bzero zpcpu window; read primitive only, no escalation", "evidence": "run.log: 'leakmark_zon 000064, 00000000, 000000, 000000, 4702111234474983745' (=0x4141414141414141) from 'su -m nobody -c sysctl vm.zone'; fix_validation.log: same run on kernel #1 gives REQUESTS 00000000; dmesg line 'zone=0xfffff80118628000 sizeof(struct vm_zone)=16640'", "kernel_refs": ["sys/vm/vm_zone.c:453", "sys/vm/vm_zone.c:347-363", "sys/vm/vm_zone.c:773", "sys/vm/vm_zone.c:786", "sys/vm/vm_zone.c:848", "sys/vm/vm_zone.c:854-858", "sys/vm/swap_pager.c:441", "sys/netbt/bt_proto.c:139-149"], "poc_changes": "self-authored harness (no seed); groom = 16x kmalloc(sizeof(struct vm_zone)) memset 0x41 then kfree, immediately followed by zinit so the freed block is reused; verified nobody can read vm.zone via 'su -m nobody -c sysctl vm.zone'", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 1140, "guest_dirty": 0, "build_cmd": "cd /root/df2829 && make (KLD: cc -O2 -D_KERNEL ... -c df2829_harness.c; cc -Wl,... -r -o df2829.ko)", "run_cmd": "kldload /root/df2829/df2829.ko && su -m nobody -c 'sysctl vm.zone' | grep leakmark", "code_hash": "fb5bfa58a5dc91d707e7f81f245dbbbfe6bdc2de65eb36e6e707dd8cbea094b5", "notes": "Same unprivileged reader surface as DF-0951 (sysctl_vm_zone). Secondary init-window issue folded in: LIST_INSERT (vm_zone.c:359) precedes bzero(z->zpcpu) (vm_zone.c:362). Stock SWAPMETA row read 00000001 at fresh boot (stale qword was 0).", "recommended_fix": "Initialize z->znalloc=0 in zinitna's init block, allocate the zone with M_ZERO in zinit(), and bzero(z->zpcpu) before LIST_INSERT_HEAD onto zlist (see fix.diff)", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Tue Sep 1 20:57:06 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Applied fix.diff to guest /usr/src/sys/vm/vm_zone.c, make nativekernel KERNCONF=X86_64_GENERIC (vm_zone.o rebuilt, kernel relinked), installkernel + reboot into #1. Identical PoC rerun (same groom/module): leakmark REQUESTS=00000000 - marker leak eliminated; no regressions in other rows.", "fix_evidence": "findings/poc/DF-2829/fix_validation.log (patched run), fix.diff, compare run.log (baseline 4702111234474983745)" } |