]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
firmware: arm_ffa: Snapshot notifier callbacks under lock
authorSudeep Holla <sudeep.holla@kernel.org>
Tue, 28 Apr 2026 18:33:34 +0000 (19:33 +0100)
committerSudeep Holla <sudeep.holla@kernel.org>
Tue, 5 May 2026 15:42:49 +0000 (16:42 +0100)
Both notification handlers currently look up a notifier callback under
notify_lock, drop the lock, and then dereference the returned
notifier entry. A concurrent unregister can delete and free that
entry in the gap, leaving the handler to dereference stale memory.

Copy the callback pointer and callback data while notify_lock is
still held and invoke the callback only after the lock is dropped.
This keeps the existing callback execution model while removing the
use-after-free window in both the framework and non-framework
notification paths.

Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications")
Link: https://patch.msgid.link/20260428-ffa_fixes-v2-10-8595ae450034@kernel.org
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
drivers/firmware/arm_ffa/driver.c

index 66ed98e32bd6aa92cdcf9aedca9f506c4b73f0c3..98ead7ed28caac616d8319f3d2b6761db34fe914 100644 (file)
@@ -1463,20 +1463,25 @@ static int ffa_notify_send(struct ffa_device *dev, int notify_id,
 
 static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
 {
+       ffa_notifier_cb cb;
+       void *cb_data;
        int notify_id;
-       struct notifier_cb_info *cb_info = NULL;
 
        for (notify_id = 0; notify_id <= FFA_MAX_NOTIFICATIONS && bitmap;
             notify_id++, bitmap >>= 1) {
                if (!(bitmap & 1))
                        continue;
 
-               read_lock(&drv_info->notify_lock);
-               cb_info = notifier_hnode_get_by_type(notify_id, type);
-               read_unlock(&drv_info->notify_lock);
+               scoped_guard(read_lock, &drv_info->notify_lock) {
+                       struct notifier_cb_info *cb_info;
+
+                       cb_info = notifier_hnode_get_by_type(notify_id, type);
+                       cb = cb_info ? cb_info->cb : NULL;
+                       cb_data = cb_info ? cb_info->cb_data : NULL;
+               }
 
-               if (cb_info && cb_info->cb)
-                       cb_info->cb(notify_id, cb_info->cb_data);
+               if (cb)
+                       cb(notify_id, cb_data);
        }
 }
 
@@ -1484,9 +1489,10 @@ static void handle_fwk_notif_callbacks(u32 bitmap)
 {
        void *buf;
        uuid_t uuid;
+       void *fwk_cb_data;
        int notify_id = 0, target;
+       ffa_fwk_notifier_cb fwk_cb;
        struct ffa_indirect_msg_hdr *msg;
-       struct notifier_cb_info *cb_info = NULL;
        size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid);
 
        /* Only one framework notification defined and supported for now */
@@ -1522,12 +1528,17 @@ static void handle_fwk_notif_callbacks(u32 bitmap)
                ffa_rx_release();
        }
 
-       read_lock(&drv_info->notify_lock);
-       cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid);
-       read_unlock(&drv_info->notify_lock);
+       scoped_guard(read_lock, &drv_info->notify_lock) {
+               struct notifier_cb_info *cb_info;
+
+               cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target,
+                                                         &uuid);
+               fwk_cb = cb_info ? cb_info->fwk_cb : NULL;
+               fwk_cb_data = cb_info ? cb_info->cb_data : NULL;
+       }
 
-       if (cb_info && cb_info->fwk_cb)
-               cb_info->fwk_cb(notify_id, cb_info->cb_data, buf);
+       if (fwk_cb)
+               fwk_cb(notify_id, fwk_cb_data, buf);
        kfree(buf);
 }