From 75f4914bf68302c5ad81b95719666c6a3e9b43bc Mon Sep 17 00:00:00 2001 From: Jianping Li Date: Thu, 25 Jun 2026 11:30:44 +0800 Subject: [PATCH] FROMLIST: misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps() DMA handles passed as invoke arguments (scalars beyond nbufs) may refer to the same dma_buf fd as an input/output buffer argument. Taking an extra reference for such DMA handle maps leads to duplicate mappings and an unbalanced reference count, since DMA handle maps are released separately when the DSP returns the fd through the fdlist. Fix this by not taking an extra reference for DMA handle arguments (take_ref = false) and tagging them with FASTRPC_MAP_DMA_HANDLE. As these maps are borrowed references, fastrpc_get_args() re-validates the map via fastrpc_map_lookup() before dereferencing it, so it is not used after being freed. fastrpc_put_args() only releases maps flagged as FASTRPC_MAP_DMA_HANDLE and clears the flag to guarantee the map is freed exactly once. Also reject FASTRPC_MAP_DMA_HANDLE in fastrpc_req_mem_map(), since such handles are already mapped implicitly during the remote invoke call and must not be mapped again through the explicit MEM_MAP path. Link: https://lore.kernel.org/all/20260716113254.570-1-jianping.li@oss.qualcomm.com/ Fixes: 10df039834f84 ("misc: fastrpc: Skip reference for DMA handles") Cc: stable@kernel.org Signed-off-by: Jianping Li --- drivers/misc/fastrpc.c | 56 +++++++++++++++++++++++++++---------- include/uapi/misc/fastrpc.h | 2 ++ 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 2150bf00f518e..8b0d93416476f 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -238,6 +238,7 @@ struct fastrpc_map { u64 len; u64 raddr; u32 attr; + u32 flags; struct kref refcount; }; @@ -869,7 +870,7 @@ static const struct dma_buf_ops fastrpc_dma_buf_ops = { }; static int fastrpc_map_attach(struct fastrpc_user *fl, int fd, - u64 len, u32 attr, struct fastrpc_map **ppmap) + u64 len, u32 attr, struct fastrpc_map **ppmap, int mflags) { struct fastrpc_session_ctx *sess = fl->sctx; struct fastrpc_map *map = NULL; @@ -886,6 +887,7 @@ static int fastrpc_map_attach(struct fastrpc_user *fl, int fd, map->fl = fl; map->fd = fd; + map->flags = mflags; map->buf = dma_buf_get(fd); if (IS_ERR(map->buf)) { err = PTR_ERR(map->buf); @@ -961,13 +963,13 @@ static int fastrpc_map_attach(struct fastrpc_user *fl, int fd, return err; } -static int fastrpc_map_create(struct fastrpc_user *fl, int fd, - u64 len, u32 attr, struct fastrpc_map **ppmap) +static int fastrpc_map_create(struct fastrpc_user *fl, int fd, u64 len, u32 attr, + struct fastrpc_map **ppmap, bool take_ref, int mflags) { - if (!fastrpc_map_lookup(fl, fd, ppmap, true)) + if (!fastrpc_map_lookup(fl, fd, ppmap, take_ref)) return 0; - return fastrpc_map_attach(fl, fd, len, attr, ppmap); + return fastrpc_map_attach(fl, fd, len, attr, ppmap, mflags); } /* @@ -1038,23 +1040,25 @@ static int fastrpc_create_maps(struct fastrpc_invoke_ctx *ctx) int i, err; for (i = 0; i < ctx->nscalars; ++i) { + bool take_ref = i < ctx->nbufs; + int mflags = 0; if (ctx->args[i].fd == 0 || ctx->args[i].fd == -1 || ctx->args[i].length == 0) continue; - if (i < ctx->nbufs) - err = fastrpc_map_create(ctx->fl, ctx->args[i].fd, - ctx->args[i].length, ctx->args[i].attr, &ctx->maps[i]); - else - err = fastrpc_map_attach(ctx->fl, ctx->args[i].fd, - ctx->args[i].length, ctx->args[i].attr, &ctx->maps[i]); + /* Set the DMA handle mapping flag for DMA handles */ + if (i >= ctx->nbufs) + mflags = FASTRPC_MAP_DMA_HANDLE; + + err = fastrpc_map_create(ctx->fl, ctx->args[i].fd, ctx->args[i].length, + ctx->args[i].attr, &ctx->maps[i], take_ref, mflags); if (err) { dev_err(dev, "Error Creating map %d\n", err); return -EINVAL; } - } + return 0; } @@ -1247,6 +1251,16 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) list[i].num = ctx->args[i].length ? 1 : 0; list[i].pgidx = i; if (ctx->maps[i]) { + /* It is possible that map is created with + * mflags FASTRPC_MAP_DMA_HANDLE and take_ref + * is false. Check if map still exists or is + * being freed as take_ref is false + */ + if (fastrpc_map_lookup(ctx->fl, ctx->args[i].fd, + &ctx->maps[i], false)) { + ctx->maps[i] = NULL; + return -EINVAL; + } pages[i].addr = ctx->maps[i]->phys; pages[i].size = ctx->maps[i]->size; } @@ -1296,8 +1310,13 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx, for (i = 0; i < FASTRPC_MAX_FDLIST; i++) { if (!fdlist[i]) break; - if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false)) + /* Validate the map flags for DMA handles and skip freeing map if invalid */ + if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false) && + mmap->flags == FASTRPC_MAP_DMA_HANDLE) { + /* Allow DMA handle maps to free only once */ + mmap->flags = 0; fastrpc_map_put(mmap); + } } return ret; @@ -1649,7 +1668,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl, fl->pd = USER_PD; if (init.filelen && init.filefd) { - err = fastrpc_map_create(fl, init.filefd, init.filelen, 0, &map); + err = fastrpc_map_create(fl, init.filefd, init.filelen, 0, &map, true, 0); if (err) goto err; } @@ -2275,8 +2294,15 @@ static int fastrpc_req_mem_map(struct fastrpc_user *fl, char __user *argp) if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; + /* + * Prevent mapping backward compatible DMA handles here, as they are + * already mapped in the remote call. + */ + if (req.flags == FASTRPC_MAP_DMA_HANDLE) + return -EINVAL; + /* create SMMU mapping */ - err = fastrpc_map_create(fl, req.fd, req.length, 0, &map); + err = fastrpc_map_create(fl, req.fd, req.length, 0, &map, true, 0); if (err) { dev_err(dev, "failed to map buffer, fd = %d\n", req.fd); return err; diff --git a/include/uapi/misc/fastrpc.h b/include/uapi/misc/fastrpc.h index ba1ea5ed426c4..c04749bf4f96a 100644 --- a/include/uapi/misc/fastrpc.h +++ b/include/uapi/misc/fastrpc.h @@ -45,6 +45,8 @@ enum fastrpc_map_flags { FASTRPC_MAP_FD = 2, FASTRPC_MAP_FD_DELAYED, FASTRPC_MAP_FD_NOMAP = 16, + /* Map the DMA handle in the invoke call for backward compatibility */ + FASTRPC_MAP_DMA_HANDLE = 0x20000, FASTRPC_MAP_MAX, };