Add reference counting using kref to the fastrpc_user structure to
prevent use-after-free issues when contexts are freed from workqueue
after device release.

The issue occurs when fastrpc_device_release() frees the user structure
while invoke contexts are still pending in the workqueue. When the
workqueue later calls fastrpc_context_free(), it attempts to access
buf->fl->cctx in fastrpc_buf_free(), leading to a use-after-free:

  pc : fastrpc_buf_free+0x38/0x80 [fastrpc]
  lr : fastrpc_context_free+0xa8/0x1b0 [fastrpc]
  ...
  fastrpc_context_free+0xa8/0x1b0 [fastrpc]
  fastrpc_context_put_wq+0x78/0xa0 [fastrpc]
  process_one_work+0x180/0x450
  worker_thread+0x26c/0x388

Implement proper reference counting to fix this:
- Initialize kref in fastrpc_device_open()
- Take a reference in fastrpc_context_alloc() for each context
- Release the reference in fastrpc_context_free() when context is freed
- Release the initial reference in fastrpc_device_release()

This ensures the user structure remains valid as long as there are
contexts holding references to it, preventing the race condition.

Fixes: 6cffd79504ce ("misc: fastrpc: Add support for dmabuf exporter")
Cc: [email protected]
Signed-off-by: Anandu Krishnan E <[email protected]>
---
 drivers/misc/fastrpc.c | 35 +++++++++++++++++++++++++++++++----
 1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 47356a5d5804..3ababcf327d7 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -310,6 +310,8 @@ struct fastrpc_user {
        spinlock_t lock;
        /* lock for allocations */
        struct mutex mutex;
+       /* Reference count */
+       struct kref refcount;
 };
 
 /* Extract SMMU PA from consolidated IOVA */
@@ -497,15 +499,36 @@ static void fastrpc_channel_ctx_put(struct 
fastrpc_channel_ctx *cctx)
        kref_put(&cctx->refcount, fastrpc_channel_ctx_free);
 }
 
+static void fastrpc_user_free(struct kref *ref)
+{
+       struct fastrpc_user *fl = container_of(ref, struct fastrpc_user, 
refcount);
+
+       fastrpc_channel_ctx_put(fl->cctx);
+       mutex_destroy(&fl->mutex);
+       kfree(fl);
+}
+
+static void fastrpc_user_get(struct fastrpc_user *fl)
+{
+       kref_get(&fl->refcount);
+}
+
+static void fastrpc_user_put(struct fastrpc_user *fl)
+{
+       kref_put(&fl->refcount, fastrpc_user_free);
+}
+
 static void fastrpc_context_free(struct kref *ref)
 {
        struct fastrpc_invoke_ctx *ctx;
        struct fastrpc_channel_ctx *cctx;
+       struct fastrpc_user *fl;
        unsigned long flags;
        int i;
 
        ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount);
        cctx = ctx->cctx;
+       fl = ctx->fl;
 
        for (i = 0; i < ctx->nbufs; i++)
                fastrpc_map_put(ctx->maps[i]);
@@ -521,6 +544,8 @@ static void fastrpc_context_free(struct kref *ref)
        kfree(ctx->olaps);
        kfree(ctx);
 
+       /* Release the reference taken in fastrpc_context_alloc() */
+       fastrpc_user_put(fl);
        fastrpc_channel_ctx_put(cctx);
 }
 
@@ -628,6 +653,8 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
 
        /* Released in fastrpc_context_put() */
        fastrpc_channel_ctx_get(cctx);
+       /* Take a reference to user, released in fastrpc_context_free() */
+       fastrpc_user_get(user);
 
        ctx->sc = sc;
        ctx->retval = -1;
@@ -658,6 +685,7 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
        spin_lock(&user->lock);
        list_del(&ctx->node);
        spin_unlock(&user->lock);
+       fastrpc_user_put(user);
        fastrpc_channel_ctx_put(cctx);
        kfree(ctx->maps);
        kfree(ctx->olaps);
@@ -1606,11 +1634,9 @@ static int fastrpc_device_release(struct inode *inode, 
struct file *file)
        }
 
        fastrpc_session_free(cctx, fl->sctx);
-       fastrpc_channel_ctx_put(cctx);
-
-       mutex_destroy(&fl->mutex);
-       kfree(fl);
        file->private_data = NULL;
+       /* Release the reference taken in fastrpc_device_open */
+       fastrpc_user_put(fl);
 
        return 0;
 }
@@ -1654,6 +1680,7 @@ static int fastrpc_device_open(struct inode *inode, 
struct file *filp)
        spin_lock_irqsave(&cctx->lock, flags);
        list_add_tail(&fl->user, &cctx->users);
        spin_unlock_irqrestore(&cctx->lock, flags);
+       kref_init(&fl->refcount);
 
        return 0;
 }
-- 
2.34.1

Reply via email to