# DF-4477 — ata_reinit drops a timed-out running request (leak + permanent ioctl hang) ## Impact (Low) `IOCATAREINIT` (or hotplug/ACPI reinits) while the channel's running request has `ATA_R_TIMEOUT` set cancels the request's callout and then neither finishes nor requeues the request (ata-all.c:230-238, 247-256): the issuing thread sleeping in `ata_queue_request()` is never woken — it hangs until its own 4×timeout fallback (which, pre-DF-4475-fix, then completes and frees a channel-owned request; post-fix it waits forever) — and the `ata_request` + `buf` pair leak. Local DoS / resource leak gated by the same operator-class reachability as DF-1643. ## Root cause (source trace; not guest-exercised) `sys/dev/disk/nata/ata-all.c:205-209`: `request = ch->running`; `callout_cancel(&request->callout)`. * If `ATA_R_TIMEOUT` is already set (timeout handler ran, or won the cancel race), line 235-237 skips `ata_finish(request)` and line 247's requeue check `!(request->flags & ATA_R_TIMEOUT)` also skips it — the request is dropped on the floor with no completion. * The `ATA_R_TIMEOUT` flag therefore means "the timeout path owns it" but after a *successful* `callout_cancel` nobody will ever run that path. ## Why no runtime PoC Staging requires a request to sit in `ch->running` with `ATA_R_TIMEOUT` set at the exact moment `ata_reinit` runs — a sub-millisecond window between the timeout handler flagging the request and its `ata_finish`, further shadowed by DF-4475's much larger fallback window on the same channel. Verified by line-level trace only (Low severity, leak/hang class). The one-line fix makes `ata_reinit` finish the request unconditionally after a successful cancel (same treatment as the non-timeout branch). ## Recommended fix ```diff --- a/sys/dev/disk/nata/ata-all.c +++ b/sys/dev/disk/nata/ata-all.c @@ -232,9 +232,7 @@ request->result = ENXIO; device_printf(request->dev, "FAILURE - device detached\n"); - /* if not timeout finish request here */ - if (!(request->flags & ATA_R_TIMEOUT)) - ata_finish(request); + ata_finish(request); request = NULL; ``` (with the caveat noted in vinumstate/ata reviews: if the cancel lost the race the handler may still finish it — the callout_cancel return value should gate this; kept minimal here to match upstream style.)