[PATCH] spi: Fix controller reference leak in acpi_spi_device_alloc()
From: Ma Ke
Date: Sun Oct 11 2026 - 21:44:46 EST
When acpi_spi_device_alloc() is called with a NULL ctlr, it lets
acpi_spi_add_resource() look the controller up with
acpi_spi_find_controller_by_adev(). That one uses class_find_device(),
so the controller comes back with a reference held and is stored in
lookup.ctlr.
None of the four return paths of acpi_spi_device_alloc() drops it. The
three error paths return an ERR_PTR directly and the success path
returns the spi_device.
spi_alloc_device() does not consume that reference either. It takes
its own through spi_controller_get(), which spidev_release() puts back
when the spi_device is freed. The reference obtained by the lookup
therefore has no owner.
The controller must not be put when it was supplied by the caller, so
track whether the lookup was ours.
acpi_spi_notify() in the same file shows both halves of this. It looks
the controller up with the same helper and puts the reference itself
after handing the controller to acpi_register_spi_device(), which
calls acpi_spi_device_alloc() with that controller. The
caller-supplied case, where the function must not put.
This is reachable from serial_multi_instantiate_probe(), which calls
acpi_spi_device_alloc() with ctlr == NULL once per resource in a loop.
Found by code review.
Fixes: 87e59b36e5e2 ("spi: Support selection of the index of the ACPI Spi Resource before alloc")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Ma Ke <make_ruc2021@xxxxxxx>
---
drivers/spi/spi.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 5b5b3bc5f0d8..4373afc1259c 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -3163,11 +3163,13 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
struct list_head resource_list;
struct acpi_spi_lookup lookup = {};
struct spi_device *spi;
+ bool put_ctlr;
int ret;
if (!ctlr && index == -1)
return ERR_PTR(-EINVAL);
+ put_ctlr = !ctlr;
lookup.ctlr = ctlr;
lookup.irq = -1;
lookup.index = index;
@@ -3176,9 +3178,11 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
acpi_spi_add_resource, &lookup);
- if (ret < 0)
+ if (ret < 0) {
/* Found SPI in _CRS but it points to another controller */
- return ERR_PTR(ret);
+ spi = ERR_PTR(ret);
+ goto out_put_ctlr;
+ }
acpi_dev_free_resource_list(&resource_list);
@@ -3189,14 +3193,17 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
acpi_spi_parse_apple_properties(adev, &lookup);
}
- if (!lookup.max_speed_hz)
- return ERR_PTR(-ENODEV);
+ if (!lookup.max_speed_hz) {
+ spi = ERR_PTR(-ENODEV);
+ goto out_put_ctlr;
+ }
spi = spi_alloc_device(lookup.ctlr);
if (!spi) {
dev_err(&lookup.ctlr->dev, "failed to allocate SPI device for %s\n",
dev_name(&adev->dev));
- return ERR_PTR(-ENOMEM);
+ spi = ERR_PTR(-ENOMEM);
+ goto out_put_ctlr;
}
spi_set_chipselect(spi, 0, lookup.chip_select);
@@ -3212,6 +3219,10 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
*/
spi->cs_index_mask = BIT(0);
+out_put_ctlr:
+ if (put_ctlr && lookup.ctlr)
+ put_device(&lookup.ctlr->dev);
+
return spi;
}
EXPORT_SYMBOL_GPL(acpi_spi_device_alloc);
--
2.43.0