DF-3584 / verdict.json
{ "finding_id": "DF-3584", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Unchecked array index confirmed at source level: isp_control() ISPCTL_RESET_DEV (sys/dev/disk/isp/isp.c:4522) and ISPCTL_ABORT_CMD (isp.c:4624) read fcp->isp_dev_map[tgt] without validating tgt against MAX_FC_TARG, unlike isp_start() (isp.c:4247-4254) which checks the same mapping. The CAM XPT_RESET_DEV glue packs the bus number into tgt (isp_freebsd.c:4519-4523 tgt |= (bus << 16)), so every BDR for a channel>0 FC device indexes at least 65536 entries past the array: read address base+151608 vs a 43152-byte 2-channel allocation \u2014 108,456 bytes out of bounds (fault proven by the extracted-logic PoC, run.log). In-kernel impact is a wild read during BDR handling: kernel panic where unmapped, otherwise a garbage index that the existing post-check usually rejects. Not exercised on the guest (no QLogic HBA / no multi-channel card). Additionally observed while tracing the caller: DragonFlys XPT_ABORT passes the XPT_ABORT CCB itself instead of the CCB to abort (isp_freebsd.c:4541 isp_control(isp, ISPCTL_RESET_DEV... vs upstreams accb), making command abort always fail the handle lookup \u2014 a functional divergence in the neighboring file, noted for that files audit.", "exploit_chain": "", "evidence": "run.log \u2014 read address = base + 20536 + 2*65536 = base + 151608, => 108456 bytes past the end of the 43152-byte allocation, FAULT (SIGSEGV/SEGV) at 0x8004c5038 => 108456 bytes past allocation end; trigger_df3584.c; VERDICT.md; fix.diff (bounds-check tgt in both ISPCTL cases)", "kernel_refs": [ "sys/dev/disk/isp/isp.c:4509-4529", "sys/dev/disk/isp/isp.c:4617-4631", "sys/dev/disk/isp/isp.c:4247-4254", "sys/dev/disk/isp/isp_freebsd.c:4517-4523" ], "poc_changes": "No seed (pass-2 new finding). Trigger written from scratch against verbatim kernel headers; one iteration needed a missing <stddef.h> include for offsetof (captured in run.log).", "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": 90, "guest_dirty": 0, "build_cmd": "cc -Wall -I. -o trigger_df3584 trigger_df3584.c (headers copied from /usr/src/sys/dev/disk/isp)", "run_cmd": "./trigger_df3584", "code_hash": "f8f16146069490592216eabd88a58fceb5e127ab4fbb41505c403178fba0c28a", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff adds the same MAX_FC_TARG bounds check isp_start() already performs, before the isp_dev_map[] load, in both ISPCTL_RESET_DEV and ISPCTL_ABORT_CMD. Baseline not reproducible without hardware; no patched-kernel A/B possible. Callers should additionally stop packing bus<<16 into tgt (isp_freebsd.c), but the isp.c-side check removes the memory-safety hazard regardless.", "fix_evidence": "fix.diff (git-apply-able against sys/dev/disk/isp/isp.c hunks at lines 4522 and 4624)", "notes": "Read-side only (post-load hdlidx was already checked); the defect is the unchecked load itself. Root cause spans two files \u2014 the index validation gap is in isp.c (this finding), the bus<<16 packing is in isp_freebsd.c (flagged for that files audit).", "recommended_fix": "Bounds-check tgt (0 <= tgt < MAX_FC_TARG) before indexing fcp->isp_dev_map[] in ISPCTL_RESET_DEV/ISPCTL_ABORT_CMD." } |