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)
PoC verification
Evidence pack
findings/poc/DF-3658 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
- Kernel-internal CAM clients get
target_idfromxpt_setup_ccb()= path target, discovery-bounded β in-bounds. Not exploitable in normal use. - Userland
/dev/xpt0(root:operator 0600) β DragonFlyxptioctl()CAMIOCOMMAND whitelist (cam_xpt.c:1028+) excludes XPT_GET/SET_TRAN_SETTINGS; demonstrated live (run.log, errno 45). - Guest has no QLogic HBA β
isp_actionunreachable 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_testablefix.diff adds the missing guards; no hardware on guest to validate behaviorally.
findings/poc/DF-3658/fix.diff
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.
No comments yet.