⬢ DragonFlyBSD Kernel Audit
DF-3584 / verdict.json
← back to finding ↓ download raw
{
  "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."
}