From 9a36fb26a8020a2064e43652956d00ca58387bf4 Mon Sep 17 00:00:00 2001 From: Zhenghang Xiao Date: Sat, 30 May 2026 21:45:28 +0100 Subject: [PATCH 1/2] BACKPORT: misc: fastrpc: fix use-after-free race in fastrpc_map_create fastrpc_map_lookup returns a raw pointer after releasing fl->lock. The caller fastrpc_map_create then calls fastrpc_map_get (kref_get_unless_zero) on this unprotected pointer. A concurrent MEM_UNMAP can free the map between the lock release and the kref operation, resulting in a use-after-free on the freed slab object. Restore the take_ref parameter to fastrpc_map_lookup so the reference is acquired atomically under fl->lock before the pointer is exposed to the caller. Link: https://lore.kernel.org/all/20260530204528.116920-5-srini@kernel.org/ Fixes: 10df039834f8 ("misc: fastrpc: Skip reference for DMA handles") Cc: stable@vger.kernel.org Signed-off-by: Zhenghang Xiao Signed-off-by: Srinivas Kandagatla --- drivers/misc/fastrpc.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 67c2ad704621..2903e07bee43 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -392,7 +392,7 @@ static int fastrpc_map_get(struct fastrpc_map *map) static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, - struct fastrpc_map **ppmap) + struct fastrpc_map **ppmap, bool take_ref) { struct fastrpc_map *map = NULL; struct dma_buf *buf; @@ -407,6 +407,12 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, if (map->fd != fd || map->buf != buf) continue; + if (take_ref) { + ret = fastrpc_map_get(map); + if (ret) + break; + } + *ppmap = map; ret = 0; break; @@ -957,19 +963,10 @@ static int fastrpc_map_attach(struct fastrpc_user *fl, int fd, static int fastrpc_map_create(struct fastrpc_user *fl, int fd, u64 len, u32 attr, struct fastrpc_map **ppmap) { - struct fastrpc_session_ctx *sess = fl->sctx; - int err = 0; + if (!fastrpc_map_lookup(fl, fd, ppmap, true)) + return 0; - if (!fastrpc_map_lookup(fl, fd, ppmap)) { - if (!fastrpc_map_get(*ppmap)) - return 0; - dev_dbg(sess->dev, "%s: Failed to get map fd=%d\n", - __func__, fd); - } - - err = fastrpc_map_attach(fl, fd, len, attr, ppmap); - - return err; + return fastrpc_map_attach(fl, fd, len, attr, ppmap); } /* @@ -1298,7 +1295,7 @@ 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)) + if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false)) fastrpc_map_put(mmap); } From eeca32ce98cadac9e74f58ac194328f993bea3f3 Mon Sep 17 00:00:00 2001 From: Jianping Li Date: Thu, 25 Jun 2026 11:30:44 +0800 Subject: [PATCH 2/2] FROMLIST: misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps() fastrpc_create_maps() performs map lookup only for buffer arguments (i < ctx->nbufs) via fastrpc_map_create(). For arguments beyond this range, no lookup is performed, which can result in duplicate DMA mappings for the same file descriptor. Additionally, if the same file descriptor is passed multiple times within a single invocation, performing lookups with reference counting would increment the reference multiple times, while fastrpc_put_args() would release it only once, leading to an imbalanced reference count. Fix this by allowing fastrpc_map_create() to control whether the lookup should take a reference. For arguments beyond ctx->nbufs, the lookup is performed without taking a reference, ensuring that existing mappings are reused without introducing duplicate DMA mappings or reference count imbalance. Link: https://lore.kernel.org/all/20260625080832.17477-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 | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 2903e07bee43..dafeed33748c 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -961,9 +961,9 @@ static int fastrpc_map_attach(struct fastrpc_user *fl, int fd, } static int fastrpc_map_create(struct fastrpc_user *fl, int fd, - u64 len, u32 attr, struct fastrpc_map **ppmap) + u64 len, u32 attr, struct fastrpc_map **ppmap, bool take_ref) { - 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); @@ -1037,23 +1037,23 @@ static int fastrpc_create_maps(struct fastrpc_invoke_ctx *ctx) int i, err; for (i = 0; i < ctx->nscalars; ++i) { + bool take_ref = true; 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]); + if (i >= ctx->nbufs) + take_ref = false; + + err = fastrpc_map_create(ctx->fl, ctx->args[i].fd, ctx->args[i].length, + ctx->args[i].attr, &ctx->maps[i], take_ref); if (err) { dev_err(dev, "Error Creating map %d\n", err); return -EINVAL; } - } + return 0; } @@ -1648,7 +1648,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); if (err) goto err; } @@ -2275,7 +2275,7 @@ static int fastrpc_req_mem_map(struct fastrpc_user *fl, char __user *argp) return -EFAULT; /* 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); if (err) { dev_err(dev, "failed to map buffer, fd = %d\n", req.fd); return err;