Skip to content

Commit 98070bb

Browse files
Anandu Krishnan Eekanshibu
authored andcommitted
FROMLIST: misc: fastrpc: fix use-after-free of fastrpc_user in workqueue context
There is a race between fastrpc_device_release() and the workqueue that processes DSP responses. When the user closes the file descriptor, fastrpc_device_release() frees the fastrpc_user structure. Concurrently, an in-flight DSP invocation can complete and fastrpc_rpmsg_callback() schedules context cleanup via schedule_work(&ctx->put_work). If the workqueue runs fastrpc_context_free() in parallel with or after fastrpc_device_release() has freed the user structure, it dereferences the freed fastrpc_user. Depending on the state of the context at the time of the race, any one of the following accesses can be hit: 1. fastrpc_buf_free() calls fastrpc_ipa_to_dma_addr(buf->fl->cctx, ...) to strip the SID bits from the stored IOVA before passing the physical address to dma_free_coherent(). 2. fastrpc_free_map() reads map->fl->cctx->vmperms[0].vmid to reconstruct the source permission bitmask needed for the qcom_scm_assign_mem() call that returns memory from the DSP VM back to HLOS. 3. fastrpc_free_map() acquires map->fl->lock to safely remove the map node from the fl->maps list. The resulting use-after-free manifests as: 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 Add kref-based reference counting to fastrpc_user. Have each invoke context take a reference on the user at allocation time and release it when the context is freed. Release the initial reference in fastrpc_device_release() at file close. Move the teardown of the user structure — freeing pending contexts, maps, mmaps, and the channel context reference — into the kref release callback fastrpc_user_free(), so that it runs only when the last reference is dropped, regardless of whether that happens at device close or after the final in-flight context completes. Link:https://lore.kernel.org/all/20260518203507.3754994-1-anandu.e@oss.qualcomm.com/ Fixes: 6cffd79 ("misc: fastrpc: Add support for dmabuf exporter") Cc: stable@kernel.org Signed-off-by: Anandu Krishnan E <anandu.e@oss.qualcomm.com>
1 parent 7e60e1e commit 98070bb

1 file changed

Lines changed: 52 additions & 22 deletions

File tree

drivers/misc/fastrpc.c

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ struct fastrpc_user {
332332
spinlock_t lock;
333333
/* lock for allocations */
334334
struct mutex mutex;
335+
/* Reference count */
336+
struct kref refcount;
335337
};
336338

337339
/* Extract SMMU PA from consolidated IOVA */
@@ -549,15 +551,57 @@ static void fastrpc_channel_ctx_put(struct fastrpc_channel_ctx *cctx)
549551
kref_put(&cctx->refcount, fastrpc_channel_ctx_free);
550552
}
551553

554+
static void fastrpc_context_put(struct fastrpc_invoke_ctx *ctx);
555+
556+
static void fastrpc_user_free(struct kref *ref)
557+
{
558+
struct fastrpc_user *fl = container_of(ref, struct fastrpc_user, refcount);
559+
struct fastrpc_invoke_ctx *ctx, *n;
560+
struct fastrpc_map *map, *m;
561+
struct fastrpc_buf *buf, *b;
562+
563+
if (fl->init_mem)
564+
fastrpc_buf_free(fl->init_mem);
565+
566+
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
567+
list_del(&ctx->node);
568+
fastrpc_context_put(ctx);
569+
}
570+
571+
list_for_each_entry_safe(map, m, &fl->maps, node)
572+
fastrpc_map_put(map);
573+
574+
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
575+
list_del(&buf->node);
576+
fastrpc_buf_free(buf);
577+
}
578+
579+
fastrpc_channel_ctx_put(fl->cctx);
580+
mutex_destroy(&fl->mutex);
581+
kfree(fl);
582+
}
583+
584+
static void fastrpc_user_get(struct fastrpc_user *fl)
585+
{
586+
kref_get(&fl->refcount);
587+
}
588+
589+
static void fastrpc_user_put(struct fastrpc_user *fl)
590+
{
591+
kref_put(&fl->refcount, fastrpc_user_free);
592+
}
593+
552594
static void fastrpc_context_free(struct kref *ref)
553595
{
554596
struct fastrpc_invoke_ctx *ctx;
555597
struct fastrpc_channel_ctx *cctx;
598+
struct fastrpc_user *fl;
556599
unsigned long flags;
557600
int i;
558601

559602
ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount);
560603
cctx = ctx->cctx;
604+
fl = ctx->fl;
561605

562606
for (i = 0; i < ctx->nbufs; i++)
563607
fastrpc_map_put(ctx->maps[i]);
@@ -572,6 +616,8 @@ static void fastrpc_context_free(struct kref *ref)
572616
kfree(ctx->olaps);
573617
kfree(ctx);
574618

619+
/* Release the reference taken in fastrpc_context_alloc() */
620+
fastrpc_user_put(fl);
575621
fastrpc_channel_ctx_put(cctx);
576622
}
577623

@@ -679,6 +725,8 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
679725

680726
/* Released in fastrpc_context_put() */
681727
fastrpc_channel_ctx_get(cctx);
728+
/* Take a reference to user, released in fastrpc_context_free() */
729+
fastrpc_user_get(user);
682730

683731
ctx->sc = sc;
684732
ctx->retval = -1;
@@ -709,6 +757,7 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
709757
spin_lock(&user->lock);
710758
list_del(&ctx->node);
711759
spin_unlock(&user->lock);
760+
fastrpc_user_put(user);
712761
fastrpc_channel_ctx_put(cctx);
713762
kfree(ctx->maps);
714763
kfree(ctx->olaps);
@@ -1689,9 +1738,6 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
16891738
{
16901739
struct fastrpc_user *fl = (struct fastrpc_user *)file->private_data;
16911740
struct fastrpc_channel_ctx *cctx = fl->cctx;
1692-
struct fastrpc_invoke_ctx *ctx, *n;
1693-
struct fastrpc_map *map, *m;
1694-
struct fastrpc_buf *buf, *b;
16951741
unsigned long flags;
16961742

16971743
fastrpc_release_current_dsp_process(fl);
@@ -1700,27 +1746,10 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
17001746
list_del(&fl->user);
17011747
spin_unlock_irqrestore(&cctx->lock, flags);
17021748

1703-
fastrpc_buf_free(fl->init_mem);
1704-
1705-
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
1706-
list_del(&ctx->node);
1707-
fastrpc_context_put(ctx);
1708-
}
1709-
1710-
list_for_each_entry_safe(map, m, &fl->maps, node)
1711-
fastrpc_map_put(map);
1712-
1713-
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
1714-
list_del(&buf->node);
1715-
fastrpc_buf_free(buf);
1716-
}
1717-
17181749
fastrpc_session_free(cctx, fl->sctx);
1719-
fastrpc_channel_ctx_put(cctx);
1720-
1721-
mutex_destroy(&fl->mutex);
1722-
kfree(fl);
17231750
file->private_data = NULL;
1751+
/* Release the reference taken in fastrpc_device_open */
1752+
fastrpc_user_put(fl);
17241753

17251754
return 0;
17261755
}
@@ -1764,6 +1793,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
17641793
spin_lock_irqsave(&cctx->lock, flags);
17651794
list_add_tail(&fl->user, &cctx->users);
17661795
spin_unlock_irqrestore(&cctx->lock, flags);
1796+
kref_init(&fl->refcount);
17671797

17681798
return 0;
17691799
}

0 commit comments

Comments
 (0)