* [PATCH v3] misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps()
@ 2026-08-05 6:09 Jianping Li
2026-08-05 6:22 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Jianping Li @ 2026-08-05 6:09 UTC (permalink / raw)
To: Srinivas Kandagatla, Ekansh Gupta
Cc: Jianping Li, Arnd Bergmann, Greg Kroah-Hartman, Sumit Semwal,
Christian König, Ling Xu, Dmitry Baryshkov, linux-arm-msm,
dri-devel, linux-kernel, linux-media, linaro-mm-sig, quic_chennak,
stable
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.
Fixes: 10df039834f84 ("misc: fastrpc: Skip reference for DMA handles")
Cc: stable@kernel.org
Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
---
Patch [v2]: https://lore.kernel.org/all/20260716113254.570-1-jianping.li@oss.qualcomm.com/
Changes in v3:
- No functional changes.
- fastrpc_put_args(): document that clearing map->flags without a lock
is safe because the DSP reports a given fd in the fdlist only once,
so no concurrent fastrpc_put_args() can race on the same map's flags.
Changes in v2:
- Rework the commit message to describe the DMA handle reference and
lifetime problem more precisely.
- Introduce a new FASTRPC_MAP_DMA_HANDLE uapi flag and a 'flags' field
in struct fastrpc_map to explicitly tag DMA handle maps, instead of
relying only on the nbufs boundary / take_ref.
- Plumb an mflags argument through fastrpc_map_create() and
fastrpc_map_attach() so DMA handle maps are tagged at creation time.
- Re-validate the borrowed map in fastrpc_get_args() via
fastrpc_map_lookup() before dereferencing it, to avoid a
use-after-free when the map was created with take_ref = false.
- In fastrpc_put_args(), only release maps tagged FASTRPC_MAP_DMA_HANDLE
and clear the flag afterwards, so such maps are freed exactly once.
- Reject FASTRPC_MAP_DMA_HANDLE in fastrpc_req_mem_map(), since these
handles are already mapped implicitly during the remote invoke and
must not be mapped again through the explicit MEM_MAP path.
---
drivers/misc/fastrpc.c | 60 +++++++++++++++++++++++++++----------
include/uapi/misc/fastrpc.h | 2 ++
2 files changed, 46 insertions(+), 16 deletions(-)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 90fd669636ec..8c98c8af6084 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -253,6 +253,7 @@ struct fastrpc_map {
u64 len;
u64 raddr;
u32 attr;
+ u32 flags;
struct kref refcount;
};
@@ -879,7 +880,7 @@ static dma_addr_t fastrpc_compute_dma_addr(struct fastrpc_user *fl, dma_addr_t s
}
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;
@@ -896,6 +897,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);
@@ -970,13 +972,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);
}
/*
@@ -1047,23 +1049,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;
}
@@ -1195,6 +1199,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]->dma_addr;
pages[i].size = ctx->maps[i]->size;
}
@@ -1244,8 +1258,17 @@ 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))
+ /*
+ * DMA handle maps are released when the DSP returns the corresponding fd in
+ * fdlist. The DSP is expected to return a specific fd only once in fdlist,
+ * so no two fastrpc_put_args() paths should clear the DMA_HANDLE flag for
+ * the same map concurrently.
+ */
+ if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false) &&
+ mmap->flags == FASTRPC_MAP_DMA_HANDLE) {
+ mmap->flags = 0;
fastrpc_map_put(mmap);
+ }
}
return ret;
@@ -1621,7 +1644,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;
}
@@ -2246,9 +2269,14 @@ 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 ba1ea5ed426c..c04749bf4f96 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,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v3] misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps()
2026-08-05 6:09 [PATCH v3] misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps() Jianping Li
@ 2026-08-05 6:22 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-08-05 6:22 UTC (permalink / raw)
To: Jianping Li; +Cc: dri-devel, media-ci
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Critical] Sharing DMA handle maps without taking a reference allows one invocation to unmap the memory while a concurrent invocation is still actively using it on the DSP, leading to IOMMU faults.
- [High] A Time-Of-Check to Time-Of-Use (TOCTOU) race condition in `fastrpc_get_args()` allows a concurrent thread to free a borrowed DMA handle map, leading to a Host-Side Use-After-Free.
- [High] A lockless Read-Modify-Write sequence on `mmap->flags` in `fastrpc_put_args()` creates a data race that can be exploited to double-free a DMA handle map.
Pre-existing issues:
- [High] DMA handle maps created during argument processing are permanently leaked if the fastrpc invocation fails on error paths or is interrupted by a signal.
--
--- Patch [1]: [PATCH v3] misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps() ---
commit 518577268f37fc91e27676af4609f0c0fa3363fc
Author: Jianping Li <jianping.li@oss.qualcomm.com>
misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps()
This patch changes DMA handle mappings to not take an extra reference
count and tags them with FASTRPC_MAP_DMA_HANDLE. It re-validates the map in
fastrpc_get_args() and modifies fastrpc_put_args() to only release maps
flagged as FASTRPC_MAP_DMA_HANDLE to avoid duplicate mappings.
Patch [v2]: https://lore.kernel.org/all/20260716113254.570-1-jianping.li@oss.qualcomm.com/
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index f3a49384586d1..59bf33cbed5af 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
> [ ... ]
> @@ -999,23 +1001,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;
[Severity: Critical]
By setting take_ref to false for DMA handles, does this allow concurrent
invocations to share a map without incrementing its reference count?
If one invocation finishes and drops the only reference in fastrpc_put_args(),
won't the SMMU mapping be torn down while another concurrent invocation is
still actively using it on the DSP?
> + 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);
[Severity: High]
This is a pre-existing issue, but does this code permanently leak DMA
handles if an error occurs after map creation?
If an error happens or a signal interrupts the process, the cleanup path
fastrpc_context_free() intentionally omits freeing maps where
i >= ctx->nbufs:
drivers/misc/fastrpc.c:fastrpc_context_free() {
...
for (i = 0; i < ctx->nbufs; i++)
fastrpc_map_put(ctx->maps[i]);
...
}
Can an unprivileged user intentionally provide invalid arguments (e.g., bad
payload size) to trigger a failure after map creation, causing an unbounded
memory leak of orphaned DMA handles?
> if (err) {
> dev_err(dev, "Error Creating map %d\n", err);
> return -EINVAL;
> }
> -
> }
> +
> return 0;
> }
> [ ... ]
> @@ -1143,6 +1147,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]->dma_addr;
[Severity: High]
Is there a race condition between looking up the map and dereferencing it?
Since fastrpc_map_lookup() is called with take_ref = false, it locklessly
returns a pointer to the shared map without holding a reference. Could a
concurrent thread finish its invocation and free this shared map before
pages[i].addr = ctx->maps[i]->dma_addr is evaluated, resulting in a host-side
use-after-free?
> + pages[i].size = ctx->maps[i]->size;
> }
> [ ... ]
> @@ -1199,8 +1213,17 @@ 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))
> + /*
> + * DMA handle maps are released when the DSP returns the corresponding fd in
> + * fdlist. The DSP is expected to return a specific fd only once in fdlist,
> + * so no two fastrpc_put_args() paths should clear the DMA_HANDLE flag for
> + * the same map concurrently.
> + */
> + if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false) &&
> + mmap->flags == FASTRPC_MAP_DMA_HANDLE) {
> + mmap->flags = 0;
> fastrpc_map_put(mmap);
> + }
[Severity: High]
Does the lockless check of mmap->flags allow a double-free if concurrent
invocations share the same fd?
If two concurrent threads from the same user process share the same DMA
handle fd, they share the same map object. When both invocations finish, they
could execute fastrpc_put_args() concurrently.
If they reach this check at the same time, couldn't they both evaluate
mmap->flags == FASTRPC_MAP_DMA_HANDLE as true, clear the flag, and call
fastrpc_map_put() twice on the same map?
> }
>
> return ret;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260805060940.41414-1-jianping.li@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-05 6:22 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05 6:09 [PATCH v3] misc: fastrpc: avoid duplicate DMA mappings in fastrpc_create_maps() Jianping Li
2026-08-05 6:22 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.