[PATCH v4 0/5] ata: Do not release the host resources twice on probe() failure
From: Niklas Cassel <cassel@kernel.org>
Date: 2026-09-10 17:14:55
Also in:
linux-arm-kernel
ata_host_start() registers ata_host_stop() as a devres action as soon as
it has succeeded:
if (have_stop) {
start_dr = devres_alloc(ata_host_stop, 0, GFP_KERNEL);
...
if (start_dr)
devres_add(host->dev, start_dr);
host->flags |= ATA_HOST_STARTED;
From that point on, releasing the host resources is owned by devres: when
probe() fails, the driver core calls devres_release_all(), which calls
ata_host_stop(), which calls ->port_stop() and ->host_stop().
ata_host_activate() and ahci_host_activate_multi_irqs() can however fail
after ata_host_start() has succeeded - devm_kasprintf(),
devm_request_irq() (a shared IRQ conflict) and ata_host_register()
(scsi_add_host(), ata_tport_add()) can all fail - and they return the
error with the devres action still registered. Since the caller cannot
tell whether ata_host_start() succeeded, and since it has to release the
resources for the failures happening before that, all the ahci-platform
drivers release the host resources in their probe() error path, e.g.
ahci_probe() calls ahci_platform_disable_resources() while
ahci_host_stop() does the same through devres.
The clocks, regulators, resets and PHYs of the host are therefore
released twice, which gives refcount underflow warnings from the clk,
regulator and phy cores and, for shared resources, can disable resources
which are still in use by other devices.
Patch 4 adds ata_host_undo_start(), which stops the ports and drops the
devres action without calling ->host_stop(), and calls it from both
activation helpers when they fail, so that "on failure, the caller
releases what it acquired" holds for all of them. sata_qstor and sata_fsl
are the only drivers which implement ->host_stop() while having no error
handling at all for the activate host call, so they get some.
Patches 1 to 3 have to come first, as they fix error paths which patch 4
would otherwise turn into resource leaks, and which are worth fixing on
their own:
- ahci_st is the only ahci-platform driver whose ->host_stop() does more
than its probe() error path, as it also asserts the "pwr-dwn" reset
(patch 1).
- sata_fsl releases hcr_base and host_priv in its probe() error path
although sata_fsl_host_stop() does that as well, which is a
use-after-free that the two device_create_file() calls after
ata_host_activate() can trigger today (patch 2).
- ahci_host_activate_multi_irqs() never frees the IRQs which it
requested when it fails, so the IRQ handlers stay registered while the
caller releases the resources of the host (patch 3).
Patch 5 is an unrelated kdoc fix that I noticed while documenting the
above.
Changes since v3:
- New patch 2, which fixes the use-after-free of host_priv in the
probe() error path of sata_fsl. Patch 4 would otherwise make sata_fsl
leak hcr_base and host_priv when activating the host fails.
- New patch 3, which frees the IRQs requested by
ahci_host_activate_multi_irqs() when it fails, like
ata_host_activate() already does.
- Reword the "if failed, just free the IRQ and leave ports alone"
comment in ata_host_activate(), as the ports are no longer left alone.
Niklas Cassel (5):
ata: ahci_st: Assert the power down reset in the probe() error path
ata: sata_fsl: Fix use-after-free of host_priv on probe() failure
ata: libahci: Free the IRQs when activating a multi-IRQ host fails
ata: libata: Do not leave ata_host_stop() registered when activation
fails
ata: libata-core: Fix the ata_host_register() kdoc
drivers/ata/ahci_st.c | 51 ++++++++++++---------
drivers/ata/libahci.c | 28 +++++++++++-
drivers/ata/libahci_platform.c | 4 ++
drivers/ata/libata-core.c | 81 ++++++++++++++++++++++++++++++----
drivers/ata/libata-sff.c | 5 +++
drivers/ata/sata_fsl.c | 24 +++++++---
drivers/ata/sata_qstor.c | 8 +++-
include/linux/libata.h | 1 +
8 files changed, 161 insertions(+), 41 deletions(-)
--
2.55.0