Commit 38290b18 authored by Sudeep Holla's avatar Sudeep Holla
Browse files

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: 285a5ea0 ("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: default avatarSudeep Holla <sudeep.holla@kernel.org>
parent 0399e3f8
Loading
Loading
Loading
Loading
+23 −12
Original line number Diff line number Diff line
@@ -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);
		scoped_guard(read_lock, &drv_info->notify_lock) {
			struct notifier_cb_info *cb_info;

			cb_info = notifier_hnode_get_by_type(notify_id, type);
		read_unlock(&drv_info->notify_lock);
			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);
}