β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3658

XPT_GET/SET_TRAN_SETTINGS index isp_devparam[tgt]/isp_dev_map[tgt] with unchecked CCB target_id (sym DF-3574 / ncr DF-3609 class)

Summary

Both transport-settings cases take tgt = cts->ccb_h.target_id with no bounds check. SET writes sdp->isp_devparam[tgt].goal_flags/goal_period/goal_offset/dev_update (16-entry array); GET reads the same fields plus fcp->isp_dev_map[tgt] (512-entry uint16_t array) - a wild read at a fully controlled 32-bit index (the hdlidx<MAX_FC_TARG guard protects only portdb[], not the map read). The XPT_SCSI_IO bounds check exists only under #ifdef DIAGNOSTIC. Kernel-internal CAM clients are path-bounded and userland /dev/xpt0 cannot inject these func codes (verified live) - hardening/defense-in-depth, consistent with DF-3574/DF-3609. If ever reached: OOB 16-bit writes at controlled offsets from the sdparam heap allocation, or panic-grade wild reads.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3658 Β· 8 files
FileTypeDescriptionSize
trig_tran.c β€” 1.9 KB view raw
build.sh β€” 90 B view raw
run.sh β€” 95 B view raw
build.log β€” 65 B view raw
run.log β€” 249 B view raw
env.txt β€” 386 B view raw
fix.diff β€” 956 B view raw
VERDICT.md β€” 2.6 KB ↓ raw
VERDICT.md
↓ download raw

DF-3658 VERDICT β€” unchecked target_id in XPT_GET/SET_TRAN_SETTINGS

Status: not_reproduced (hardening-class; both userland and HW routes absent on guest)

Analysis

Sinks (all in sys/dev/disk/isp/isp_freebsd.c, all indexed by tgt = cts->ccb_h.target_id with no bounds check):

Line Sink Array Bound
4585 sdp->isp_devparam[tgt].goal_flags (RMW write) sdparam.isp_devparam[16] (ispvar.h:195) 16 (SCSI)
4616-4617 .goal_period, .goal_offset (16-bit writes) same 16
4623 .dev_update (write) same 16
4649 fcp->isp_dev_map[tgt] (read, 32-bit index) uint16_t[512] (ispvar.h:485) 512 (FC)
4664-4673 .actv_* / .nvrm_* (reads) same sdparam 16

A crafted CCB with target_id β‰₯ 16 (SCSI branch) yields OOB 16-bit writes at a controlled offset from the sdparam heap allocation (which is isp_param, sized sizeof(sdparam)*nchan, allocated in isp_pci.c:830+); β‰₯ 512 on FC yields a wild read at fcparam + 2*tgt β€” tgt is 32-bit so the read address is fully controlled β†’ panic-capable.

hdlidx < MAX_FC_TARG at 4650 guards only portdb[hdlidx], not the isp_dev_map[tgt] read itself.

Reachability

  1. Kernel-internal CAM clients get target_id from xpt_setup_ccb() = path target, discovery-bounded β†’ in-bounds. Not exploitable in normal use.
  2. Userland /dev/xpt0 (root:operator 0600) β€” DragonFly xptioctl() CAMIOCOMMAND whitelist (cam_xpt.c:1028+) excludes XPT_GET/SET_TRAN_SETTINGS; demonstrated live (run.log, errno 45).
  3. Guest has no QLogic HBA β†’ isp_action unreachable regardless.

Conclusion: defensive-depth finding, same class as DF-3574 (sym) and DF-3609 (ncr). Severity Info, confidence certain (the missing check is unambiguous; the exploit route requires a kernel-internal CCB forger).

Fix

fix.diff: validate tgt >= ISP_MAX_TARGETS(isp) in both XPT_SET_TRAN_SETTINGS and XPT_GET_TRAN_SETTINGS and complete the CCB with CAM_TID_INVALID. ISP_MAX_TARGETS(isp) returns 512 for FC / 16 for SCSI, covering both the sdparam and isp_dev_map sinks.

Fix validation

not_testable β€” no isp hardware on the guest; no userland injection route (whitelist). The diff is a two-hunk guard matching the existing DIAGNOSTIC check at isp_freebsd.c:4325.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff adds the missing guards; no hardware on guest to validate behaviorally.

findings/poc/DF-3658/fix.diff
↓ fix.diffper-fix-DF-3658

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (userland XPT_GET_TRAN_SETTINGS rejected pre-SIM errno 45); VERDICT.md sink table with path:line; fix.diff adds tgt >= ISP_MAX_TARGETS(isp) guards

PoC changes

wrote reachability trigger from scratch; loop only reached tgt=0 due to loop bound typo - single rejection line still demonstrates the whitelist

Verified recommended fix

Bounds-check tgt against ISP_MAX_TARGETS(isp) in XPT_SET_TRAN_SETTINGS and XPT_GET_TRAN_SETTINGS, completing out-of-range requests with CAM_TID_INVALID.

Verdict

isp_action XPT_SET_TRAN_SETTINGS writes sdp->isp_devparam[tgt].{goal_flags,goal_period,goal_offset,dev_update} (isp_freebsd.c:4585,4616-4623) and XPT_GET_TRAN_SETTINGS reads isp_devparam[tgt].actv_/nvrm_ (4664-4673) and fcp->isp_dev_map[tgt] (4649) using the CCB-supplied target_id with no bounds check (arrays: 16 / 512 entries). Kernel-internal CAM clients are path-bounded and DragonFly's /dev/xpt0 CAMIOCOMMAND whitelist excludes these func codes (verified live, errno 45), so exploitation needs a kernel-internal CCB forger - hardening class identical to DF-3574/DF-3609. Guest additionally has no QLogic hardware, so no runtime manifestation: not_reproduced, impact none on this guest.