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
