⬢ DragonFlyBSD Kernel Audit
DF-2263 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2263",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "likely",
  "verdict": "Source defect certain and reproduced as a real queue-handling failure: with three bios queued and ERR|EOF pending, the real sastart completes bio1 once and returns while bio2 and bio3 remain validly queued but are never biodone (their physio callers would biowait until a later sastart), softc->queue_count is left desynced at 2, and SA_FLAG_ERR_PENDING is cleared although the pending condition still covers the stranded bios. The control-flow claim is verified line-accurately: no refresh of the local bio exists between the goto at 1625 and the again label at 1603, so per source semantics the loop re-processes the removed and completed bio (stale bioq_remove, rewrite of bp->b_resid and b_error on a completed physio buffer, second biodone). However on the stock INVARIANTS gcc 8.3 -O2 kernel the compiled loop exits after the first completion instead of re-entering, so the unbounded loop, the queue_count underflow beyond the initial desync, and the double-biodone bpdone KASSERT panic did not manifest on this binary; both manifestations exist in the machine code (QUEUEDEBUG Bad-link panic path and bpdone already-done KASSERT are compiled in) and are reachable whenever the loop re-enters, eg under a different compiler or optimization level. Honest net: deterministic tape I/O starvation hang plus driver state corruption for the queued bios, privileged reach, no privesc.",
  "exploit_chain": "",
  "evidence": "run_2263_unfixed.dmesg and run_2263a_unfixed.dmesg: SUMMARY bio#1=1 bio#2=0 bio#3=0 final queue_count=2 on both scenarios including real getpbuf buffers with biodone_sync; the after-biodone line shows bioq_first=B2 (queue non-empty) while the function nevertheless returned; VERDICT.md carries the disassembly comparison showing the fix instruction mov 0x10(r12),rbx present at the loop back-edge only in the patched build",
  "kernel_refs": [
    "sys/bus/cam/scsi/scsi_sa.c:1601",
    "sys/bus/cam/scsi/scsi_sa.c:1603",
    "sys/bus/cam/scsi/scsi_sa.c:1605",
    "sys/bus/cam/scsi/scsi_sa.c:1607",
    "sys/bus/cam/scsi/scsi_sa.c:1623",
    "sys/bus/cam/scsi/scsi_sa.c:1624",
    "sys/bus/cam/scsi/scsi_sa.c:1625",
    "sys/bus/cam/scsi/scsi_sa.c:1631",
    "sys/kern/kern_physio.c:32",
    "sys/kern/vfs_bio.c:3519"
  ],
  "poc_changes": "Replaced the seeded sketch with a kernel-compiled harness: verbatim scsi_sa.c concatenated between df_pre.c interposition preamble and df_post.c driver; scenario A uses real getpbuf_mem buffers with biodone_sync exactly as physio queues them; scenario B uses a counting re-arming completion callback with a rescue that terminates the loop after 2000 re-completions; xpt_schedule also interposed because the 1718 tail call panics on the unowned SIM lock in the fabricated setup",
  "attempts": 9,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #10: Sat Oct 17 20:52:24 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (stock INVARIANTS kernel family; harness builds #2-#10)",
  "runtime_sec": 9600,
  "guest_dirty": 1,
  "build_cmd": "./build.sh (nativekernel KERNCONF=X86_64_GENERIC with harness TU, nativeinstall, reboot); iteration via ./quick.sh",
  "run_cmd": "vm.sh run_root sysctl debug.df226x.t2263b=1 ; vm.sh run_root sysctl debug.df226x.t2263a=1 ; vm.sh run_root dmesg | grep DFH2263",
  "code_hash": "c5eb0e5a2741c97028f40821deddc7ce6676d21a4f77706c83dd1c749148fb69",
  "notes": "No panic occurred in either scenario on this binary, so panic.txt is not present; the double-biodone INVARIANTS manifestation is documented as compiler-dependent with its KASSERT locations cited (vfs_bio.c:3519-3521). Runtime A/B fix validation inconclusive in this rig (identical observable single-pass behavior on patched and unpatched kernels); fix validated at machine-code level: the refresh instruction appears at the loop back-edge only in the patched build, and no regression appeared in the DF-2264 and DF-2265 fixed runs on the same patched kernel. privesc per team directive: none; reach requires an open tape device with EOF senses under pipelined reads, the write-after-completion window is confined to the single sastart call, and on this binary the re-entry does not execute; classify local DoS and correctness corruption on tape systems",
  "recommended_fix": "Refresh the bio pointer before re-entering the drain loop: bio = bioq_first(softc bio_queue) immediately before goto again in the EOF-pending branch of sastart",
  "fix_status": "fixed",
  "fix_evidence": "disassembly A/B: mov 0x10(r12),rbx refresh present at sastart loop back-edge only in patched build; no behavioral regression on patched kernel; runtime A/B inconclusive (rig artifact documented in VERDICT.md)"
}