sym_action2 XPT_GET/SET_TRAN_SETTINGS index np->target[]/lunmp[] without the bounds check used by every other sym entry point
Summary
sym_action2() handles XPT_SET/GET_TRAN_SETTINGS by directly indexing np->target[ccb_h->target_id] and tp->lunmp[lun] with no SYM_CONF_MAX_TARGET(16)/MAX_LUN(64) validation, unlike sym_action and sym_reset_dev. An out-of-range target would read OOB tinfo bytes into the caller-visible ccb_trans_settings (small info leak); a wildcard LUN (0xFFFF) computes &lunmp[0xFFFF] (512KB past the array) and dereferences it. Currently NOT reachable: traced every delivery path (pass CCBs, /dev/xpt0 CAMIOCOMMAND func restrictions, XPT_SET interception, async_update skip, phantom-target SCSI_IO bounds check). Defense-in-depth for any future CAM change. Hardware-absent guest: source-level only, not_reproduced.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3574 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.4 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| fix.diff | β | 1.2 KB | view raw | |
| verdict.json | β | 3.3 KB | view raw | |
| env.txt | β | 975 B | view raw |
DF-3574 β sym_action2 lacks target/LUN bounds validation before indexing np->target[] / lunmp[]
File: sys/dev/disk/sym/sym_hipd.c (pass 2, GLM 5.3 re-audit)
What
sym_action2() handles XPT_SET_TRAN_SETTINGS and XPT_GET_TRAN_SETTINGS by
indexing driver tables directly from the CCB header:
- sym_hipd.c:8048
tp = &np->target[ccb_h->target_id];(SET) - sym_hipd.c:8054
lp = sym_lp(np, tp, ccb_h->target_lun);(SET) - sym_hipd.c:8076
tp = &np->target[ccb_h->target_id];(GET) - sym_hipd.c:8077
lp = sym_lp(np, tp, ccb_h->target_lun);(GET)
sym_lp() (sym_hipd.c:1269-1271) expands to tp->lunmp[(lun)] for any
non-zero LUN. np->target[] has SYM_CONF_MAX_TARGET (16) entries and
lunmp[] has SYM_CONF_MAX_LUN (64) pointer entries.
The SCSI-IO entry point sym_action() does validate
(sym_hipd.c:7501-7506), as does sym_reset_dev() (sym_hipd.c:7438-7443).
sym_action2() has no equivalent check. A CCB carrying
target_id >= 16 (or a wildcard 0xFF/0xFFFF) would read out of bounds;
the GET path copies the out-of-bounds tinfo/usrflags bytes back into the
ccb_trans_settings returned to the caller (kernel-memory info leak of a few
bytes), and a wildcard LUN indexes lunmp[0xFFFF] β an OOB read 512 KB past
the array whose result is dereferenced (lp->current_flags,
sym_hipd.c:8083/8087) β wild pointer dereference.
Reachability (why this is Info / speculative)
Every currently-reachable delivery path was traced in this audit:
pass(4)CAMIOCOMMAND βxpt_merge_ccb()does not copytarget_id/target_lunfrom the user CCB; they are set byxpt_setup_ccb()(cam_xpt.c:3911-3918) from the periph path, i.e. a real discovered device (target 0..15, lun 0..63 on sym)./dev/xpt0CAMIOCOMMAND only accepts SCAN_BUS/RESET_BUS/PATH_INQ/ ENG_INQ/SCAN_LUN/DEBUG (cam_xpt.c:1028-1050) β never TRAN_SETTINGS.XPT_SET_TRAN_SETTINGSis intercepted byxpt_set_transfer_settings()(cam_xpt.c:3051-3056) and re-issued on the same (real-device) path.- The internal wildcard-LUN path created by the driver itself
(
sym_xpt_async_transfer_neg, sym_hipd.c:3454) only feedsxpt_async(AC_TRANSFER_NEG)βxpt_set_transfer_settings(..., async_update=TRUE), which skips both the XPT_GET_TRAN_SETTINGS round-trip and the directsim_action()call (cam_xpt.c: tail of xpt_set_transfer_settings). - Phantom targets created by
xpt_compile_path()(DragonFly's version does NOT wildcard-guard target/lun β cam_xpt.c:3973-4045 β unlike FreeBSD) never get past the probe INQUIRY becausesym_action()rejects the SCSI_IO with CAM_DEV_NOT_THERE, so no TRAN_SETTINGS CCB follows.
So today the missing check is latent: no constructible path delivers an
out-of-range target/lun to sym_action2(). It is filed as defense-in-depth
(the neighbouring entry points all validate; any future CAM change β e.g. a
new sender of TRAN_SETTINGS on wildcard paths β would turn this into an OOB
read/info leak).
PoC
Not runnable: the trigger path does not exist today, and the guest has no Symbios/LSI 53C8xx PCI HBA (virtio only), so the driver cannot even attach. Source-level verification only. See VERDICT.md.
Fix
fix.diff β add the same bounds check sym_action() uses to the two
TRAN_SETTINGS cases (not a blanket early return: XPT_RESET_BUS legally
arrives with wildcard target from /dev/xpt0).
DF-3574 β VERDICT (pass 2 audit of sys/dev/disk/sym/sym_hipd.c)
STATUS: not_reproduced (source-verified; hardware absent; no trigger path exists today) IMPACT: none (latent OOB read / info-leak hardening gap) CONFIDENCE: speculative (the missing check is certain; exploitability today is nil)
Narrative
sym_action2() (sym_hipd.c:8026) handles XPT_SET_TRAN_SETTINGS and XPT_GET_TRAN_SETTINGS. Both cases index driver tables straight from the CCB header without the bounds validation that the sibling entry points perform:
sym_hipd.c:8048 / 8076: tp = &np->target[ccb_h->target_id]; sym_hipd.c:8054 / 8077: lp = sym_lp(np, tp, ccb_h->target_lun); -> sym_hipd.c:1269-1271: tp->lunmp[(lun)]
contrast with:
sym_action() sym_hipd.c:7501-7506 (target/lun bounds -> CAM_DEV_NOT_THERE) sym_reset_dev() sym_hipd.c:7438-7443 (same)
np->target[] has 16 entries (SYM_CONF_MAX_TARGET, sym_conf.h:96); lunmp[] has 64 entries (SYM_CONF_MAX_LUN, sym_conf.h:105). An out-of-range target_id (>=16, or CAM_TARGET_WILDCARD 0xFF) reads past np->target[]; on the GET path the stale/OOB tinfo bytes are copied into the ccb_trans_settings returned to the caller (cts__spi->sync_period/offset/bus_width/ppr_options, sym_hipd.c:8095-8098, dflags at 8105-8112) β a small kernel-memory info leak. A non-zero out-of-range lun (e.g. CAM_LUN_WILDCARD 0xFFFF) computes &tp->lunmp[0xFFFF] β 512 KB past the array β and dereferences the loaded "lcb_p" (lp->current_flags / lp->user_flags) β wild pointer read.
Why it is NOT exploitable today (traced exhaustively)
- pass(4): xpt_merge_ccb (cam_xpt.c:1818-1830) copies only retry_count/func_code/timeout/flags + payload; target_id/target_lun come from xpt_setup_ccb (cam_xpt.c:3911-3918) off the periph's REAL path. passsendccb never validates cdb_ptr but does not influence target/lun.
- /dev/xpt0 CAMIOCOMMAND accepts only SCAN_BUS/RESET_BUS/PATH_INQ/ ENG_INQ/SCAN_LUN/DEBUG (cam_xpt.c:1031-1047) β no TRAN_SETTINGS.
- XPT_SET_TRAN_SETTINGS from xpt_action is intercepted (cam_xpt.c:3051) by xpt_set_transfer_settings(), which re-issues XPT_GET_TRAN_SETTINGS on the SAME path (async_update == FALSE branch) β the user's real-device path β and finally calls sim_action with the same CCB/path.
- The driver's own wildcard-lun path (sym_xpt_async_transfer_neg, sym_hipd.c:3454-3455, CAM_LUN_WILDCARD) only reaches xpt_set_transfer_settings(..., async_update=TRUE) via AC_TRANSFER_NEG (cam_xpt.c:4733), and async_update=TRUE skips BOTH the internal GET and the direct sim_action call β verified in the tail of xpt_set_transfer_settings (cam_xpt.c).
- DragonFly's xpt_compile_path (cam_xpt.c:3973-4045), unlike FreeBSD's, allocates phantom targets/devices for arbitrary/wildcard ids (no CAM_TARGET_WILDCARD guard); but a phantom target never yields a TRAN_SETTINGS CCB because its first XPT_SCSI_IO is rejected by sym_action's bounds check (CAM_DEV_NOT_THERE) and the probe stops there.
Conclusion: latent hardening gap, zero currently-constructible trigger. Filed as Info / defense-in-depth so the invariant "SIM validates what it indexes" does not depend on CAM internals.
Guest verification
Guest up (DragonFly 6.5-DEVELOPMENT X86_64_GENERIC). /boot/kernel/sym.ko exists and 'pci/sym' is registered in the module alias table, but no NCR/LSI 53C8xx PCI function exists (virtio + isa only), so the driver cannot attach and sym_action2 cannot be invoked at all. No runtime test possible β source-level certainty is the bar for this file, per the audit plan. No panic/corruption was produced or expected.
Fix
findings/poc/DF-3574/fix.diff adds the sym_action()-style bounds check to the two TRAN_SETTINGS cases of sym_action2 (a blanket early-return would wrongly reject XPT_RESET_BUS, which legitimately arrives with a wildcard target from /dev/xpt0). Not build-validated: no sym hardware to attach a patched kernel to; the diff is mechanical and mirrors 7501-7506.
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 authored against the read-only sys/ tree (never applied). Not build-validated: the fix guards a latent path and the guest has no sym adapter to attach a patched kernel; the change is mechanical, mirroring sym_hipd.c:7501-7506. A blanket early-return in sym_action2 was deliberately avoided because XPT_RESET_BUS legitimately arrives with a wildcard target from /dev/xpt0.
findings/poc/DF-3574/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-3574/VERDICT.md (full reachability trace with cam_xpt.c cites); findings/poc/DF-3574/fix.diff; findings/poc/DF-3574/env.txt (guest: sym.ko present, no PCI 53C8xx, cannot attach)
PoC changes
No PoC seed existed; no runnable trigger path exists (latent hardening gap) and the guest lacks sym hardware, so no PoC was authored. Environment probe recorded instead (env.txt).
Verified recommended fix
Add sym_action()-style target/lun bounds checks to the XPT_SET_TRAN_SETTINGS and XPT_GET_TRAN_SETTINGS cases of sym_action2() (see findings/poc/DF-3574/fix.diff).
Verdict
sym_action2() indexes np->target[ccb_h->target_id] and sym_lp(np,tp,ccb_h->target_lun) (sym_hipd.c:8048/8054/8076/8077 -> lunmp[lun] at 1269-1271) without the SYM_CONF_MAX_TARGET/MAX_LUN bounds check that sym_action (7501-7506) and sym_reset_dev (7438-7443) perform. A wild target/lun would OOB-read tinfo bytes into the caller-visible ccb_trans_settings (small info leak) and a wildcard LUN computes &lunmp[0xFFFF] and dereferences it (wild read). Every currently-constructible delivery path was traced (pass real-device paths; xptioctl func-code restrictions; XPT_SET intercepted by xpt_set_transfer_settings; the driver's wildcard-lun async path uses async_update=TRUE which skips both the GET round-trip and the sim_action call; phantom targets die at sym_action's SCSI_IO bounds check) β no reachable trigger exists today, and the guest has no 53C8xx HBA so the driver cannot even attach. Filed as defense-in-depth; fix.diff mirrors the sym_action check in the two TRAN_SETTINGS cases.
No comments yet.