# DF-4701 — twa(4): `tw_cl_shutdown_ctlr` leaves a half-shut-down, still-attached controller when the firmware handshake fails; no `ctlr->active` gate on the I/O entry paths

- **File**: `sys/dev/raid/twa/tw_cl_init.c` (pass 2)
- **Severity**: Low · **Class**: improper shutdown state machine / missing liveness gate (CWE-667 / CWE-362)
- **PoC status**: `untested` — requires a physical 3ware 9000-series PCI/PCIe controller
  (vendor 0x13C1, device 0x1002–0x1005). The audit QEMU guest has no such device
  (see `env.txt`): `twa` never attaches there, so neither the baseline nor a patched
  kernel can exercise the path on the guest.

## What the bug is

`twa_detach()` (tw_osl_freebsd.c:813-815) calls `twa_shutdown()` and **aborts the
detach if it returns an error**. `twa_shutdown()` calls
`tw_cl_shutdown_ctlr(handle, 0)` (tw_osl_freebsd.c:853), which:

1. marks the controller inactive (`ctlr->active = TW_CL_FALSE`, tw_cl_init.c:569),
2. disables interrupts (tw_cl_init.c:571; the OSL already did `bus_teardown_intr`,
   tw_osl_freebsd.c:846, and cancelled the watchdog callouts, :849-850),
3. sends the shutdown message via `tw_cli_init_connection` (tw_cl_init.c:574-576) —
   on a wedged/hung firmware this times out and returns an error,
4. **destroys the CL locks unconditionally, even after the failed handshake**
   (`tw_osl_destroy_lock(gen_lock/io_lock)`, tw_cl_init.c:587-588),
5. returns the handshake error (tw_cl_init.c:591).

The caller then aborts the detach — but the device is still fully attached: the CAM
sim is still registered, `/dev/twa0` still exists (both are only torn down later in
`tw_osli_free_resources`, tw_osl_freebsd.c:703/777-779, which was skipped).

Nothing re-arms the driver: interrupts stay torn down, the watchdog stays cancelled,
`ctlr->active` stays FALSE — yet **the only place in the driver that checks
`ctlr->active` is the interrupt handler** (tw_cl_intr.c:81). Neither
`twa_action()`/XPT_SCSI_IO (tw_osl_cam.c:357-393) nor `twa_ioctl()` →
`tw_osli_fw_passthru` (tw_osl_freebsd.c:147-175, 895+) nor `tw_cl_start_io()` /
`tw_cl_fw_passthru()` (tw_cl_io.c:70, 261) checks it. Every subsequent I/O request
is therefore submitted to a controller the driver has already told to go down.

## How to reproduce (needs the hardware)

See `trigger.sh`. On a machine with a 3ware 9000/9550/9650/9690 card:

1. Wedge or half-wedge the firmware (e.g. `tw_cli` maintenance operation that hangs
   the microcontroller, or a controller that is failing) — the handshake in
   `tw_cli_init_connection` must time out (`TW_CLI_REQUEST_TIMEOUT_PERIOD`).
2. As root: `kldunload twa` (or unbind via devctl). `twa_detach` → `twa_shutdown` →
   `tw_cl_shutdown_ctlr` errors → detach aborts, device half-dead but attached.
   Expected marker: `tw_cl_init_connection failed` / `init_connection failed` event
   (tw_cl_init.c:700-704) followed by `Failed to shutdown Common Layer/controller`
   (tw_osl_freebsd.c:854-859) and `kldunload: kldunload(...) returned 5`.
3. As an **unprivileged** user, issue I/O against any filesystem on the twa volume
   (`ls -lR /twa-volume`). Expected: commands stall for the CAM timeout periods and
   error out; on INVARIANTS/debug kernels the spin-lock state assertions may fire.
   The volume stays broken until reboot.

A second-order instance of the same defect: `twa_shutdown` is the
`device_shutdown` handler (tw_osl_freebsd.c:202). CAM issues SYNCHRONIZE CACHE
flushes during system shutdown; if `twa_shutdown` runs first, those XPT_SCSI_IO
CCBs are submitted to a controller that was already told to go down with 1 message
credit and torn-down interrupts.

## Why this is Low

Triggering the broken state requires root (`kldunload`) **plus** a controller whose
firmware fails the shutdown handshake. What the defect then buys is that the
consequences (dead volume, CAM timeout storms, debug-kernel assertions) are
sustained by plain unprivileged disk I/O — there is no self-healing path (watchdog
cancelled) and no gate refusing new work.

## Fix

`fix.diff` (authored, never applied to `sys/`): once `tw_cl_shutdown_ctlr` has torn
the CL context down (locks destroyed, `active=FALSE`) it must not report an error
that makes the OSL abort the detach with the device half-dead — the failure is
already logged as an event; and the two CL entry points gain an `ctlr->active`
bail-out so a shut-down controller cleanly refuses work instead of submitting to a
dead firmware queue.
