Skip to content

Commit b3036d0

Browse files
author
Anandu Krishnan E
committed
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 dd29e69 commit b3036d0

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
@@ -329,6 +329,8 @@ struct fastrpc_user {
329329
spinlock_t lock;
330330
/* lock for allocations */
331331
struct mutex mutex;
332+
/* Reference count */
333+
struct kref refcount;
332334
};
333335

334336
static void fastrpc_free_map(struct kref *ref)
@@ -500,15 +502,57 @@ static void fastrpc_channel_ctx_put(struct fastrpc_channel_ctx *cctx)
500502
kref_put(&cctx->refcount, fastrpc_channel_ctx_free);
501503
}
502504

505+
static void fastrpc_context_put(struct fastrpc_invoke_ctx *ctx);
506+
507+
static void fastrpc_user_free(struct kref *ref)
508+
{
509+
struct fastrpc_user *fl = container_of(ref, struct fastrpc_user, refcount);
510+
struct fastrpc_invoke_ctx *ctx, *n;
511+
struct fastrpc_map *map, *m;
512+
struct fastrpc_buf *buf, *b;
513+
514+
if (fl->init_mem)
515+
fastrpc_buf_free(fl->init_mem);
516+
517+
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
518+
list_del(&ctx->node);
519+
fastrpc_context_put(ctx);
520+
}
521+
522+
list_for_each_entry_safe(map, m, &fl->maps, node)
523+
fastrpc_map_put(map);
524+
525+
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
526+
list_del(&buf->node);
527+
fastrpc_buf_free(buf);
528+
}
529+
530+
fastrpc_channel_ctx_put(fl->cctx);
531+
mutex_destroy(&fl->mutex);
532+
kfree(fl);
533+
}
534+
535+
static void fastrpc_user_get(struct fastrpc_user *fl)
536+
{
537+
kref_get(&fl->refcount);
538+
}
539+
540+
static void fastrpc_user_put(struct fastrpc_user *fl)
541+
{
542+
kref_put(&fl->refcount, fastrpc_user_free);
543+
}
544+
503545
static void fastrpc_context_free(struct kref *ref)
504546
{
505547
struct fastrpc_invoke_ctx *ctx;
506548
struct fastrpc_channel_ctx *cctx;
549+
struct fastrpc_user *fl;
507550
unsigned long flags;
508551
int i;
509552

510553
ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount);
511554
cctx = ctx->cctx;
555+
fl = ctx->fl;
512556

513557
for (i = 0; i < ctx->nbufs; i++)
514558
fastrpc_map_put(ctx->maps[i]);
@@ -523,6 +567,8 @@ static void fastrpc_context_free(struct kref *ref)
523567
kfree(ctx->olaps);
524568
kfree(ctx);
525569

570+
/* Release the reference taken in fastrpc_context_alloc() */
571+
fastrpc_user_put(fl);
526572
fastrpc_channel_ctx_put(cctx);
527573
}
528574

@@ -632,6 +678,8 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
632678

633679
/* Released in fastrpc_context_put() */
634680
fastrpc_channel_ctx_get(cctx);
681+
/* Take a reference to user, released in fastrpc_context_free() */
682+
fastrpc_user_get(user);
635683

636684
ctx->sc = sc;
637685
ctx->retval = -1;
@@ -662,6 +710,7 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
662710
spin_lock(&user->lock);
663711
list_del(&ctx->node);
664712
spin_unlock(&user->lock);
713+
fastrpc_user_put(user);
665714
fastrpc_channel_ctx_put(cctx);
666715
kfree(ctx->maps);
667716
kfree(ctx->olaps);
@@ -1627,9 +1676,6 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
16271676
{
16281677
struct fastrpc_user *fl = (struct fastrpc_user *)file->private_data;
16291678
struct fastrpc_channel_ctx *cctx = fl->cctx;
1630-
struct fastrpc_invoke_ctx *ctx, *n;
1631-
struct fastrpc_map *map, *m;
1632-
struct fastrpc_buf *buf, *b;
16331679
unsigned long flags;
16341680

16351681
fastrpc_release_current_dsp_process(fl);
@@ -1638,27 +1684,10 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
16381684
list_del(&fl->user);
16391685
spin_unlock_irqrestore(&cctx->lock, flags);
16401686

1641-
fastrpc_buf_free(fl->init_mem);
1642-
1643-
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
1644-
list_del(&ctx->node);
1645-
fastrpc_context_put(ctx);
1646-
}
1647-
1648-
list_for_each_entry_safe(map, m, &fl->maps, node)
1649-
fastrpc_map_put(map);
1650-
1651-
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
1652-
list_del(&buf->node);
1653-
fastrpc_buf_free(buf);
1654-
}
1655-
16561687
fastrpc_session_free(cctx, fl->sctx);
1657-
fastrpc_channel_ctx_put(cctx);
1658-
1659-
mutex_destroy(&fl->mutex);
1660-
kfree(fl);
16611688
file->private_data = NULL;
1689+
/* Release the reference taken in fastrpc_device_open */
1690+
fastrpc_user_put(fl);
16621691

16631692
return 0;
16641693
}
@@ -1702,6 +1731,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
17021731
spin_lock_irqsave(&cctx->lock, flags);
17031732
list_add_tail(&fl->user, &cctx->users);
17041733
spin_unlock_irqrestore(&cctx->lock, flags);
1734+
kref_init(&fl->refcount);
17051735

17061736
return 0;
17071737
}

0 commit comments

Comments
 (0)