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 <[email protected]>
---
 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

Reply via email to