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

btdone/btreset panic() on HBA-supplied btstat and reset-state bytes (hostile-controller panic family)

Summary

btdone switches on bccb->hccb.btstat - a byte written into CCB/mailbox DMA memory by the host adapter - and calls panic() for INVALID_ACTION_CODE, INVALID_OPCODE, LINKED_CCB_LUN_MISMATCH and INVALID_CCB_OR_SG_PARAM. btreset likewise panics when a device holding DIAG_ACTIVE never drops it within 10s. All five sites turn device-fed state into an immediate kernel panic while holding bt->lock. Same class as the amd.c hostile-target family (DF-4042/4043), reachable from the shared interrupt path. Error-complete-instead-of-panic fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4098 Β· 16 files
FileTypeDescriptionSize
README.md β€” 1.4 KB ↓ raw
VERDICT.md β€” 3.0 KB ↓ raw
dfbt_rig.c β€” 26.9 KB view raw
dfbt_ctl.c β€” 8.0 KB view raw
Makefile β€” 127 B ↓ download
push_rig.sh β€” 975 B view raw
run_panic_btdone.sh β€” 393 B view raw
run_panic_btreset.sh β€” 390 B view raw
run_panic_btdone.log β€” 724 B view raw
run_panic_btreset.log β€” 160 B view raw
panic_btdone.txt β€” 2.4 KB view raw
panic_btreset.txt β€” 789 B view raw
env.txt β€” 249 B view raw
fix.diff β€” 6.0 KB view raw
manifest.json β€” 839 B view raw
verdict.json β€” 2.8 KB view raw

DF-4098 β€” btdone/btreset panic() on host-adapter-fed bytes

Verdict: REPRODUCED (two independent panic sites, both captured)

Same rig class as DF-2429 (see DF-2429/README.md): production handlers resolved from the running kernel image and dispatched over a fabricated post-attach softc; the virtual firmware writes status bytes into the CCB DMA memory and the register file exactly as a MultiMaster HBA would.

Build

./push_rig.sh

Run (each on a fresh boot)

sh run_panic_btdone.sh    # site 1: btdone btstat panic
sh run_panic_btreset.sh   # site 2: btreset DIAG_ACTIVE panic

Expected output

  1. run_panic_btdone.sh: a perfectly valid I/O CCB (normal TUR, cmd_len=6, queued ACTIVE through the real XPT path) is completed by the HBA with BMBI_ERROR + btstat=BTSTAT_INVALID_ACTION_CODE (0x15) β€” a single device-fed byte β€” and production btdone executes panic("bt9: Invalid Action code") at bt.c:1517 while holding bt->lock. panic_btdone.txt has the backtrace (btdone+0x4d3).
  2. run_panic_btreset.sh: with the virtual STATUS_REG holding DIAG_ACTIVE (0x80) stuck, production btreset(hard) waits 5s for diagnostics to start (passes), then 10s for DIAG_ACTIVE to drop, and panics at bt.c:1650-1651: panic: bt9: btreset - Diagnostic Active failed to drop. status = 0x80 (panic_btreset.txt, btreset+0x266). ~15s of DELAY polling then panic.
VERDICT.md
↓ download raw

DF-4098 VERDICT β€” hostile/wedged-controller panic family in btdone/btreset

Bottom line

REPRODUCED at both cited panic sites, with the production code executing from the shipped kernel image:

  • btdone site (bt.c:1517): a valid, normally-queued I/O CCB whose completion mailbox carries btstat = BTSTAT_INVALID_ACTION_CODE (0x15) β€” one host-adapter-fed byte β€” panics the kernel while the driver holds bt->lock: panic: bt9: Invalid Action code, backtrace btdone+0x4d3 (panic_btdone.txt). The sibling panics at bt.c:1520 (INVALID_OPCODE, exercised in DF-4097), bt.c:1524 and bt.c:1527 are the same pattern (four panic() calls switching on bccb->hccb.btstat, a byte in CCB DMA memory the adapter owns).
  • btreset site (bt.c:1650-1651): with STATUS_REG scripting DIAG_ACTIVE stuck (wedged/hardware-failing controller β€” exactly the situation a reset is meant to recover from), production btreset(hard) panics after the 10s wait: panic: bt9: btreset - Diagnostic Active failed to drop. status = 0x80, backtrace btreset+0x266 (panic_btreset.txt).

Live evidence

run_panic_btdone.log + panic_btdone.txt:

[rig] COMP: firmware writes completion: bccb=0xfffff801167df620
       comp_code=0x04 btstat=0x15 sdstat=0x00 -> production btdone()
panic: bt9: Invalid Action code
btdone() at btdone+0x4d3 0xffffffff8042e763

run_panic_btreset.log + panic_btreset.txt:

[rig] BTRESET: production btreset(hard=1)
panic: bt9: btreset - Diagnostic Active failed to drop. status = 0x80
btreset() at btreset+0x266 0xffffffff8042f606

Root cause (path:line)

  • bt.c:1517, 1520, 1524, 1527 β€” panic() on bccb->hccb.btstat values written into CCB DMA memory by the host adapter.
  • bt.c:1650-1651 β€” panic() when DIAG_ACTIVE never drops within 10s.
  • Both reachable from the shared interrupt path (bt_intr β†’ bt_intr_locked β†’ btdone) and from timeout recovery (bttimeout β†’ btreset).

Impact

Any BusLogic controller that misbehaves (PCI error, wedged firmware, or a malicious/compromised HBA β€” the DF-4042/4043 amd hostile-target family analogue) converts one status byte into an immediate kernel panic. Local DoS from device-fed state; recovery is impossible because the driver dies instead of completing the command with an error.

Fix validation

fix.diff converts all five panic sites into device_printf + error completion (CAM_REQ_CMP_ERR / ETIMEDOUT). Patched bt.c+btreg.h compile clean with the X86_64_GENERIC kernel flags under -Werror (FIXED-COMPILES-CLEAN, bt.fixed.o). No runtime A/B kernel build (panic class, not memory corruption; not mandated).

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 reproduced→ patch + rebuild →patched clean

object-compile validation only (panic class, kernel A/B not mandated): patched bt.c compiles clean under -Werror with kernel headers

fix.diff; 'FIXED-COMPILES-CLEAN' with bt.fixed.o on the guest
↓ fix.diffper-fix-DF-4098

Confirmed kernel references

Detail

Exploit chain

compromised/wedged BusLogic HBA (or target-induced error) writes btstat 0x15/0x16 into the CCB completion or holds DIAG_ACTIVE -> bt_intr->bt_intr_locked->btdone panic at bt.c:1517 (or bttimeout->btreset panic at bt.c:1650) -> kernel DoS while holding bt->lock

Evidence (decisive lines)

panic_btdone.txt (panic + btdone+0x4d3 backtrace after comp_code=0x04 btstat=0x15); panic_btreset.txt (panic + btreset+0x266 after 10s DIAG_ACTIVE stuck); run_panic_*.log for the dispatch context

PoC changes

rig stands in for missing PCI hardware only; the virtual HBA scripts STATUS_REG=0x80 stuck (vhba_enable=0) for the btreset variant; both panics needed separate boots (one panic per boot)

Verified recommended fix

Replace all five panic() sites with device_printf + error completion (CAM_REQ_CMP_ERR / ETIMEDOUT) -- never trust HBA-fed bytes or reset state with the kernel's life (fix.diff, object-compile validated)

Verdict

REPRODUCED at both cited panic sites with production code from the shipped kernel image: (1) btdone panics ('panic: bt9: Invalid Action code', bt.c:1517, btdone+0x4d3) on a valid I/O CCB completed with the single device-fed btstat byte BTSTAT_INVALID_ACTION_CODE (0x15) -- three sibling panics at bt.c:1520/1524/1527 switch on the same HBA-owned byte (1520 exercised live in DF-4097); (2) btreset panics ('btreset - Diagnostic Active failed to drop', bt.c:1650, btreset+0x266) when the wedged controller holds DIAG_ACTIVE -- precisely the condition a reset exists to recover from. Hostile/wedged-controller -> immediate kernel panic, interrupt-path reachable.