⬢ DragonFlyBSD Kernel Audit
DF-5647 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5647",
  "status": "reproduced",
  "verdict": "Negative hint values (portsize/msize = -1 with positive base, or port/maddr = -1 with positive size) pass isahint.c:63/70s start>0||count>0 guard; isa_set_resource then stores rle entries with sign-extended UINT64_MAX start/count and wrapped end = start+count-1 < start. Live-verified in guest kernel memory via isa_probe_nomatch prints: atkbdc1 port 0x60-0x5e, wbsio0 port 0x2e-0x2c, lm0 iomem 0xd0000-0xcfffe and port 0xffffffffffffffff-0xe (inverted ranges), against clean controls (wbsio1 0x4e-0x4f, it0-3). The atkbdc1 line proves rman GRANTED the inverted region: rid1 (0x64) is only added after the rid0 default alloc returned non-NULL, i.e. rman_reserve_resource(0x60,0x5e,2^64-1) succeeded via subr_rman.c:236-238s wrap-satisfying (rend-rstart+1)>=count and double-claimed port 0x60 already allocated to atkbdc0. Persistent poisoned rle (lm0 iomem) plus transient rman free-list corruption (duplicated boundary byte, unsorted list) per subr_rman.c:276-291. Bookkeeping/hardware-claim corruption inside properly-allocated structures \u2014 no adjacent-object memory-safety violation demonstrated. Root/loader-gated: Low severity.",
  "reproduced": true,
  "impact": "kernel resource bookkeeping corruption (inverted rle/rman ranges, overlapping/double-claimed I/O ports)",
  "confidence": "certain",
  "exploit_chain": "loader.conf.local negative hints -> resource_kenv strtol (no range check, kenv overrides compiled devtab) -> isahint_add_device guard misses negative count/start -> bus_set_resource -> isa_set_resource end=start+count-1 wraps (isa_common.c:910) -> poisoned rle persists in child idev->id_resources -> on any default-range alloc, resource_list_allocs count=ulmax(count,rle->count) (subr_bus.c:2698-2701) feeds 2^64-1 into rman_reserve_resource -> inverted region granted, split-in-three duplicates the boundary byte into two free regions and breaks list ordering (subr_rman.c:276-291); allocations can overlap already-claimed ports (atkbdc1 over atkbdc0s 0x60).",
  "evidence": "run.log (full verbose dmesg of the poisoned boot; decisive lines: atkbdc1 ... port 0x60-0x5e,0x64, lm0 ... port 0xffffffffffffffff-0xe iomem 0xd0000-0xcfffe, wbsio0 ... 0x2e-0x2c vs controls wbsio1 ... 0x4e-0x4f, it0-3); run.fixed.log (patched kernel: poisoned devices print bare failed to probe on isa1, controls unchanged); VERDICT.md (full math + negative results)",
  "kernel_refs": [
    "sys/bus/isa/isahint.c:59-64",
    "sys/bus/isa/isahint.c:66-71",
    "sys/kern/subr_bus.c:2306-2326",
    "sys/bus/isa/isa_common.c:909-911",
    "sys/kern/subr_bus.c:2693-2701",
    "sys/kern/subr_rman.c:224-238",
    "sys/kern/subr_rman.c:266-291"
  ],
  "poc_changes": "Replaced seeded idea with loader.conf kenv hints (proven pattern): the isahint value path only reads kenv for COMPILED-IN devtab names (atkbdc/lm/wbsio/it/sio/adv/vga in X86_64_GENERIC), so the hints target those names; boot_verbose=YES added so isa_probe_nomatch prints the live rle ranges.",
  "attempts": 2,
  "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": 160,
  "guest_dirty": 0,
  "build_cmd": "none (loader.conf kenv hint trigger; see trigger.hints / run.sh)",
  "run_cmd": "cp trigger.hints /boot/loader.conf.local && shutdown -r now && dmesg | grep failed to probe",
  "code_hash": "b0ecd14ce7f323a1896772ebfc26ce8494533d3992256bf5895ffd1dd57e9a57",
  "notes": "Reproduced identically on two independent boots (one after a full vm.sh reset). Guest isahint children exist but are invisible in plain devinfo (unattached, dd_state < DS_INPROGRESS; use devinfo -v). sio unit renumbering on this guest (ACPI sio0 pre-empts unit 0, isahint children shift +1) explains historical cant drain lines; no disable-bypass bug. Guest reset to clean-source snapshot after verification.",
  "recommended_fix": "Reject negative geometry at isahint.c:63/70: require start >= 0 && count >= 0 in addition to the existing start>0||count>0 (keeps compiled port-only hints with count=0 working).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "Same fix.diff as DF-5646 (shared isahint.c patch, kernel #1 Fri Oct 9, BUILD_OK): reboot with the identical poison set armed shows bare failed to probe on isa1 for atkbdc1/lm0/wbsio0 (no ranges stored) while compiled hints still work (wbsio1 0x4e-0x4f, it0-3, sio 0x3f8/0x2f8-0x2ff) \u2014 inverted ranges gone, no regression.",
  "fix_evidence": "run.fixed.log (patched-kernel current-boot dmesg segment); build.log (kernel build + installkernel + BUILD_OK tail); diff in fix.diff"
}