[PATCH v1 1/4] ACPI: glue: Carry out companion lookup under bus_type_sem
From: Rafael J. Wysocki
Date: Thu Sep 10 2026 - 13:58:52 EST
From: "Rafael J. Wysocki" <rafael.j.wysocki@xxxxxxxxx>
When acpi_device_notify() looks up an ACPI companion for the given
device, it invokes callbacks from struct acpi_bus_type() without
synchronization which may lead to a use-after-free if the driver
module containing those callbacks is unloaded at the same time.
Address this by holding bus_type_sem throughout the entire ACPI
companion lookup and the execution of the .setup() callback in
struct acpi_bus_type (if present) instead of dropping the semaphore
(prematurely) after finding a matching struct acpi_bus_type.
For this purpose, rename acpi_get_bus_type() to acpi_companion_lookup(),
make it return a struct acpi_device pointer, and move the relevant code
from acpi_device_notify() to it.
Also notice that the only case in which the .bind() callback from an
ACPI scan handler may need to be invoked is when the given device is
a platform one, so adjust acpi_device_notify() accordingly and drop
the "done" label that is not used any more from it.
Fixes: 2ef5236660b6 ("ACPI: glue: Look for ACPI bus type only if ACPI companion is not known")
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
---
Applies on top of
https://lore.kernel.org/linux-acpi/12989369.O9o76ZdvQC@rafael.j.wysocki/
which is in linux-pm.git/linux-next now.
Thanks!
---
drivers/acpi/glue.c | 61 +++++++++++++++++++++------------------------
1 file changed, 29 insertions(+), 32 deletions(-)
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index a47cccc4efd3..40e6513a9942 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -59,19 +59,34 @@ int unregister_acpi_bus_type(struct acpi_bus_type *type)
}
EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
-static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
+static struct acpi_device *acpi_companion_lookup(struct device *dev)
{
- struct acpi_bus_type *tmp, *ret = NULL;
+ struct acpi_bus_type *type;
- down_read(&bus_type_sem);
- list_for_each_entry(tmp, &bus_type_list, list) {
- if (tmp->match(dev)) {
- ret = tmp;
- break;
+ guard(rwsem_read)(&bus_type_sem);
+
+ list_for_each_entry(type, &bus_type_list, list) {
+ struct acpi_device *adev;
+
+ if (!type->match(dev))
+ continue;
+
+ adev = type->find_companion(dev);
+ if (!adev) {
+ dev_dbg(dev, "ACPI companion not found\n");
+ return NULL;
}
+ if (acpi_bind_one(dev, adev)) {
+ dev_dbg(dev, "Binding to ACPI companion failed\n");
+ return NULL;
+ }
+ if (type->setup)
+ type->setup(dev);
+
+ return adev;
}
- up_read(&bus_type_sem);
- return ret;
+
+ return NULL;
}
#define FIND_CHILD_MIN_SCORE 1
@@ -360,40 +375,22 @@ void acpi_device_notify(struct device *dev)
ret = acpi_bind_one(dev, NULL);
if (ret) {
- struct acpi_bus_type *type = acpi_get_bus_type(dev);
-
- if (!type)
+ adev = acpi_companion_lookup(dev);
+ if (!adev)
return;
-
- adev = type->find_companion(dev);
- if (!adev) {
- dev_dbg(dev, "ACPI companion not found\n");
- return;
- }
- ret = acpi_bind_one(dev, adev);
- if (ret) {
- dev_dbg(dev, "Binding to ACPI companion failed\n");
- return;
- }
- if (type->setup) {
- type->setup(dev);
- goto done;
- }
} else {
adev = ACPI_COMPANION(dev);
if (dev_is_pci(dev)) {
pci_acpi_setup(dev, adev);
- goto done;
} else if (dev_is_platform(dev)) {
acpi_configure_pmsi_domain(dev);
+
+ if (adev->handler && adev->handler->bind)
+ adev->handler->bind(dev);
}
}
- if (adev->handler && adev->handler->bind)
- adev->handler->bind(dev);
-
-done:
dev_dbg(dev, "Bound to ACPI device %s\n", acpi_dev_name(adev));
}
--
2.51.0