[PATCH v2 10/11] firmware: arm_ffa: Snapshot notifier callbacks under lock

Sudeep Holla
2026-04-28T18:34:36Z

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")
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_ffa/driver.c | 35 +++++++++++++++++++++++------------
1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index 9181cc752ce1..2e9820395162 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -1465,20 +1465,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);
}
}

@@ -1486,9 +1491,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 */
@@ -1524,12 +1530,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);
}

--
2.43.0