DF-2263 / verdict.json
{ "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)" } |