mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 20:54:03 +02:00
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: d3cd7c525f ("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>
This commit is contained in:
parent
a14dd8fe0a
commit
2c4097e6c4
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user