firmware: arm_ffa: Snapshot notifier callbacks under lock

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: 285a5ea0f5 ("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>
This commit is contained in:
Sudeep Holla 2026-04-28 19:33:34 +01:00
parent 0399e3f872
commit 38290b180a

View 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;
if (cb_info && cb_info->cb)
cb_info->cb(notify_id, cb_info->cb_data);
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)
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;
if (cb_info && cb_info->fwk_cb)
cb_info->fwk_cb(notify_id, cb_info->cb_data, buf);
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 (fwk_cb)
fwk_cb(notify_id, fwk_cb_data, buf);
kfree(buf);
}