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

No CCB timeout handler (callout block #if 0'd, 'XXX Need a timeout handler') plus ignored bus_dmamap error β€” lost commands wedge the SIM queue forever

Summary

trm_ExecuteSRB never arms ccb_h.timeout_ch - the only code is '#if 0 /* XXX Need a timeout handler */' and trmtimeout does not exist in the tree. A command accepted by a target but never completed (mid-phase stall; the selection-timeout path only covers selection) freezes the single-command-per-target SIM queue with no recovery. The dmamap callback also ignores its error argument, so a failed bus_dmamap_load orphans the CCB with no xpt_done - unrecoverable without a timeout. Any local I/O through trm(4) with a hung/unplugged/hostile SCSI target -> permanent queue wedge (local DoS until reboot).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3831 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.7 KB ↓ raw
VERDICT.md β€” 2.3 KB ↓ raw
verdict.json β€” 2.5 KB view raw
fix.diff β€” 2.3 KB view raw
env.txt β€” 1.1 KB view raw
manifest.json β€” 598 B view raw

DF-3831 β€” trm(4) has no CCB timeout handler (callout commented out) β€” lost commands wedge the SIM queue forever

  • File: sys/dev/disk/trm/trm.c (pass 2)
  • Class: CWE-1089 (missing recovery path / unbounded wait) β€” Local DoS
  • Severity: Low (requires a hung/malicious SCSI target on a Tekram bus)

Build / Run / Expected

Hardware-gated (no Tekram HBA on the audit guest β€” env.txt). On hardware, the trigger is any command that is accepted by a target but never completed (device that stalls the data phase, yanked disk, malicious SCSI target emulation):

# start I/O through trm(4), then hang the target (or unplug it mid-transfer)
dd if=/dev/da0 of=/dev/null bs=1m

Expected on vulnerable code: the CCB never times out β€” CAM's periph error recovery (which relies on ccb_h.timeout_ch armed by the SIM) never fires; the trb SIM stays wedged until reboot.

Root cause

trm_ExecuteSRB β€” trm.c:470-474:

static void
trm_ExecuteSRB(...)
{
    ...
#if 0
    /* XXX Need a timeout handler */
    callout_reset(&ccb->ccb_h.timeout_ch, (ccb->ccb_h.timeout * hz) / 1000,
              trmtimeout, srb);
#endif
    trm_SendSRB(pACB, pSRB);

The driver author never implemented the timeout. There is also no ccb_h.timeout_ch callout_stop in the completion path (trm_SRBdone) β€” consistent with it never being armed.

Aggravator: trm_ExecuteSRB ignores the error argument of the bus_dmamap callback (trm.c:414) β€” if bus_dmamap_load fails (e.g. EFBIG for a >33-segment pass(4) buffer against TRM_NSEG = btoc(MAXPHYS)+1, trm.h:98), the CCB is either orphaned (never queued, never done) or run with zero data, and without a timeout there is no recovery in either case.

VERDICT.md
↓ download raw

VERDICT β€” DF-3831

status: inconclusive (source-certain, hardware-gated) β€” reproduced: 0 β€” guest impact: none.

Trace

  1. The only timeout machinery in the whole driver is the #if 0 block at trm.c:470-474, annotated "XXX Need a timeout handler" β€” trmtimeout does not exist anywhere in the tree (grep -rn trmtimeout sys/ -> only that comment).
  2. CAM contract: the SIM arms ccb->ccb_h.timeout_ch when it accepts a CCB; the periph layer (da/pass) depends on that callout to drive error recovery. With it never armed, a command that the chip never completes (target stops responding mid-phase β€” the exact scenario the selection-timeout logic at trm.c:1217-1229 only partially covers: it handles selection timeouts, not mid-transfer stalls) leaves the SRB on pDCB->pGoingSRB forever and the SIM queue frozen forever (single-command-per-target activation, trm.c:338-376).
  3. Aggravator (same no-recovery family): the bus_dmamap_load callback trm_ExecuteSRB(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) at trm.c:414 never inspects error. On a load failure the CCB is orphaned without xpt_done β€” and with no timeout there is no path that ever completes it -> permanent hang of that device queue plus a leaked SRB slot.

Why inconclusive, not false-positive

The missing callout is unambiguous in source (the author's own XXX concedes it). It cannot be exercised on the audit guest because trm(4) requires a Tekram DC395U/U2 PCI adapter (trm.c:3677-3688) and the guest has none (env.txt). On hardware the failure needs a hung/removed/malicious target β€” ordinary operational hazard for parallel SCSI.

Fix

fix.diff arms the ccb timeout callout in trm_ExecuteSRB, stops it in trm_SRBdone, adds the missing error check for the dmamap callback (fail the CCB instead of orphaning it), and adds a minimal trm_timeout that resets the bus (reusing the existing trm_reset machinery). git-apply --check passes against sys/dev/disk/trm/trm.c.

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

Both baseline and patched runs need an attached Tekram adapter with a hangable target; absent on the audit guest. fix.diff is git-apply-able (verified).

fix.diff
↓ fix.diffper-fix-DF-3831

Confirmed kernel references

Detail

Exploit chain

any local I/O through a trm(4) disk whose target hangs mid-phase -> CCB never times out -> SIM queue frozen permanently -> all I/O to that bus wedges (local DoS). No privilege gain.

Evidence (decisive lines)

VERDICT.md (trace of the #if 0 block and the ignored error arg); env.txt; fix.diff (arm/stop callout + trm_timeout + error check, git-apply --check passes)

PoC changes

No runnable PoC without the PCI card; pack is analysis + fix only.

Verified recommended fix

Arm ccb_h.timeout_ch in trm_ExecuteSRB, stop it in trm_SRBdone, add trm_timeout (bus reset), and fail the CCB when the dmamap callback reports error (fix.diff).

Verdict

trm(4) never arms the CAM CCB timeout: the callout_reset for ccb_h.timeout_ch is inside '#if 0 / XXX Need a timeout handler /' (sys/dev/disk/trm/trm.c:470-474) and no trm_timeout function exists in the tree. A command accepted by a target but never completed (mid-transfer stall, unplugged disk, hostile target emulator - the selection-timeout logic at trm.c:1217-1229 only covers selection, not mid-phase stalls) therefore wedges the single-command-per-target SIM queue forever with no recovery short of reboot. Aggravator: trm_ExecuteSRB ignores the bus_dmamap callback 'error' argument (trm.c:414), so a failed bus_dmamap_load (e.g. EFBIG for >TRM_NSEG=btoc(MAXPHYS)+1 segment pass(4) buffers, trm.h:98) orphans the CCB with no xpt_done - and with no timeout there is no path that ever completes it. Source-certain; hardware-gated (no Tekram PCI device on the audit guest, env.txt), hence inconclusive rather than reproduced.