[PATCH] misc: fastrpc: hold rpmsg device while using endpoint

From: Yousef Alhouseen

Date: Wed Jun 24 2026 - 15:25:37 EST


Invoke and remote heap setup paths dereference cctx->rpdev after only a
lockless NULL check. fastrpc_rpmsg_remove() can clear that pointer while
an ioctl is preparing an invoke, leaving the send path to dereference a
NULL or stale rpmsg device pointer.

Take a device reference while holding the channel lock before using the
rpmsg device. Return -EPIPE if the rpmsg endpoint has already gone away.

Signed-off-by: Yousef Alhouseen <alhouseenyousef@xxxxxxxxx>
---
drivers/misc/fastrpc.c | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 1b70acc10..4b258dbcd 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -486,9 +486,24 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
static int fastrpc_remote_heap_alloc(struct fastrpc_user *fl, struct device *dev,
u64 size, struct fastrpc_buf **obuf)
{
- struct device *rdev = &fl->cctx->rpdev->dev;
+ struct fastrpc_channel_ctx *cctx = fl->cctx;
+ struct rpmsg_device *rpdev;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&cctx->lock, flags);
+ rpdev = cctx->rpdev;
+ if (rpdev)
+ get_device(&rpdev->dev);
+ spin_unlock_irqrestore(&cctx->lock, flags);
+
+ if (!rpdev)
+ return -EPIPE;

- return __fastrpc_buf_alloc(fl, rdev, size, obuf);
+ ret = __fastrpc_buf_alloc(fl, &rpdev->dev, size, obuf);
+ put_device(&rpdev->dev);
+
+ return ret;
}

static void fastrpc_channel_ctx_free(struct kref *ref)
@@ -1260,6 +1275,7 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx,
struct fastrpc_channel_ctx *cctx;
struct fastrpc_user *fl = ctx->fl;
struct fastrpc_msg *msg = &ctx->msg;
+ struct rpmsg_device *rpdev;
unsigned long flags;
bool put_ref = true;
int ret;
@@ -1279,10 +1295,18 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx,
fastrpc_context_get(ctx);

spin_lock_irqsave(&cctx->lock, flags);
+ rpdev = cctx->rpdev;
+ if (!rpdev) {
+ spin_unlock_irqrestore(&cctx->lock, flags);
+ fastrpc_context_put(ctx);
+ return -EPIPE;
+ }
+ get_device(&rpdev->dev);
ctx->sent = true;
spin_unlock_irqrestore(&cctx->lock, flags);

- ret = rpmsg_send(cctx->rpdev->ept, (void *)msg, sizeof(*msg));
+ ret = rpmsg_send(rpdev->ept, (void *)msg, sizeof(*msg));
+ put_device(&rpdev->dev);

if (ret) {
spin_lock_irqsave(&cctx->lock, flags);
@@ -1312,9 +1336,6 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
if (!fl->sctx)
return -EINVAL;

- if (!fl->cctx->rpdev)
- return -EPIPE;
-
if (handle == FASTRPC_INIT_HANDLE && !kernel) {
dev_warn_ratelimited(fl->sctx->dev, "user app trying to send a kernel RPC message (%d)\n", handle);
return -EPERM;
--
2.54.0