ata_reinit drops a running request already flagged ATA_R_TIMEOUT after callout_cancel: request/buffer leak and permanently hung issuer
Summary
If the grabbed running request already has ATA_R_TIMEOUT, both the finish and requeue paths skip it - after a successful callout_cancel nobody ever completes it: the issuing thread hangs forever and the request+buf leak. Sub-millisecond staging window, shadowed by DF-4475 on the same channel; source-traced with the sole timeout-setter cross-referenced. Finish-after-successful-cancel fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4477 Β· 1 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | verdict | source trace + one-line fix | 2.3 KB | β raw |
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
--- 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.)
Fix verification
not_testablenot_testable: no runtime repro (source-traced Low finding); the one-line fix in README.md is behavior-preserving for the non-timeout path
N/A
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md/VERDICT.md source trace with path:line citations; recommended one-line fix included
PoC changes
N/A (no runtime PoC; README.md carries the trace and fix)
Verified recommended fix
Finish the request unconditionally after a successful callout_cancel (see README.md diff)
Verdict
Source-traced Low finding: ata_reinit (ata-all.c:205-256) cancels the running request's callout and, when ATA_R_TIMEOUT is already set, neither finishes nor requeues the request -- after a successful callout_cancel nobody will ever complete it, so the issuer hangs until (pre-DF-4475-fix) the unsafe fallback or (post-fix) forever, and the request+buf leak. Not guest-exercised: the staging window (request in ch->running with ATA_R_TIMEOUT set at reinit time) is sub-millisecond and is shadowed by DF-4475's much wider fallback window on the same channel. All cited lines verified against the tree.
No comments yet.