summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@kernel.org>2026-07-22 10:52:50 +0100
committerSudeep Holla <sudeep.holla@kernel.org>2026-07-22 11:15:35 +0100
commit2c4097e6c4aed276c5e9ec2ab331ab397ea780bf (patch)
tree1b6f23bf7d411f7a28c71e92d97382b9536e8616
parenta14dd8fe0a95db638c550ed984cfe2a7428c783d (diff)
downloadlinux-next-2c4097e6c4aed276c5e9ec2ab331ab397ea780bf.tar.gz
linux-next-2c4097e6c4aed276c5e9ec2ab331ab397ea780bf.zip
firmware: arm_scmi: Fix requested device removal race
scmi_protocol_device_unrequest() drops scmi_requested_devices_mtx while notifying listeners but continues to retain the per-protocol list head. When two SCMI drivers for the same protocol unregister concurrently, one thread can remove the final request and free the list head while the other is running its notifier. The latter then dereferences the freed list head after reacquiring the mutex and can free it a second time. Complete the list and IDR updates, including freeing an empty list head, before dropping the mutex. Keep the blocking notifier outside the critical section and retain only the detached request across the callback. Fixes: d3cd7c525fd2 ("firmware: arm_scmi: Refactor protocol device creation") Reported-by: Sashiko <sashiko-bot@kernel.org> Link: https://patch.msgid.link/20260722095250.2011630-1-sudeep.holla@kernel.org Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
-rw-r--r--drivers/firmware/arm_scmi/bus.c26
1 files changed, 13 insertions, 13 deletions
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 7f06d56e4905..cdaea09d9611 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -158,6 +158,7 @@ static int scmi_protocol_table_register(const struct scmi_device_id *id_table)
*/
static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)
{
+ struct scmi_requested_dev *rdev, *victim = NULL;
struct list_head *phead;
pr_debug("Unrequesting SCMI device (%s) for protocol %x\n",
@@ -166,29 +167,28 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table
mutex_lock(&scmi_requested_devices_mtx);
phead = idr_find(&scmi_requested_devices, id_table->protocol_id);
if (phead) {
- struct scmi_requested_dev *victim, *tmp;
-
- list_for_each_entry_safe(victim, tmp, phead, node) {
- if (!strcmp(victim->id_table->name, id_table->name)) {
- list_del(&victim->node);
-
- mutex_unlock(&scmi_requested_devices_mtx);
- blocking_notifier_call_chain(&scmi_requested_devices_nh,
- SCMI_BUS_NOTIFY_DEVICE_UNREQUEST,
- (void *)victim->id_table);
- kfree(victim);
- mutex_lock(&scmi_requested_devices_mtx);
+ list_for_each_entry(rdev, phead, node) {
+ if (!strcmp(rdev->id_table->name, id_table->name)) {
+ victim = rdev;
+ list_del(&rdev->node);
break;
}
}
- if (list_empty(phead)) {
+ if (victim && list_empty(phead)) {
idr_remove(&scmi_requested_devices,
id_table->protocol_id);
kfree(phead);
}
}
mutex_unlock(&scmi_requested_devices_mtx);
+
+ if (victim) {
+ blocking_notifier_call_chain(&scmi_requested_devices_nh,
+ SCMI_BUS_NOTIFY_DEVICE_UNREQUEST,
+ (void *)victim->id_table);
+ kfree(victim);
+ }
}
static void