* [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes
@ 2026-06-09 2:59 Jianping Li
2026-06-09 2:59 ` [PATCH v8 1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool Jianping Li
` (4 more replies)
0 siblings, 5 replies; 15+ messages in thread
From: Jianping Li @ 2026-06-09 2:59 UTC (permalink / raw)
To: Srinivas Kandagatla, Amol Maheshwari
Cc: Jianping Li, Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa,
Jorge Ramirez-Ortiz, linux-arm-msm, dri-devel, linux-kernel,
ekansh.gupta, quic_chennak
Add missing bug fixes in memory areas. This patch series fixes multiple memory
handling issues in the FastRPC driver, primarily around the Audio PD remote heap.
The Audio PD uses a reserved memory-region that is shared between HLOS
and the DSP. Allocating and freeing this memory from userspace is unsafe,
as the kernel cannot reliably determine when the DSP has finished using
the buffers.
To address this, the entire reserved memory-region for the Audio PD is
now fully assigned to the DSP during remoteproc boot-up, and its lifetime
is tied to the rpmsg channel.
Patch [v7]: https://lore.kernel.org/all/20260602071750.526-1-jianping.li@oss.qualcomm.com/
Change in v8:
- Squashed "Fail Audio PD init when reserved memory is missing" into
"Allocate entire reserved memory for Audio PD in probe" as the
validation check depends on the probe allocation
- Fixed error path in probe: use goto err_free_data instead of bare
return when kzalloc_obj fails
- Added kfree(data->remote_heap) in err_free_data path
- Made kfree(cctx->remote_heap) unconditional in rpmsg_remove,
not tied to vmcount or qcom_scm_assign_mem result
- Used local cctx variable consistently instead of fl->cctx
Change in v7:
- Removed duplicate remote heap validation check; keep it only at
the beginning of fastrpc_init_create_static_process()
Change in v6:
- Separate the handling of err_copy
- Place the check for remote_heap at the beginning of the function
Change in v5:
- Split reserved-memory handling into separate patches
Change in v4:
- Fail Audio PD static process creation when no reserved memory-region
is present, instead of silently proceeding
Change in v3:
- Adjusted the order of the series, placing NULL check changes that are not bug fixes at the end
- Modified the commit message to describe the bug background in detail
- Switch buf->list_lock back to fl->lock
- Add locking to the operation of audio_init_mem
Changes in v2:
- Remove the if check outside fastrpc_buf_free
- Store the spinlock pointer in the struct fastrpc_buf instead
- Allocate entire reserved memory to audio PD through remote heap
Ekansh Gupta (3):
misc: fastrpc: Fix initial memory allocation for Audio PD memory pool
misc: fastrpc: Remove buffer from list prior to unmap operation
misc: fastrpc: Allow fastrpc_buf_free() to accept NULL
Jianping Li (1):
misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
drivers/misc/fastrpc.c | 137 ++++++++++++++++++++++-------------------
1 file changed, 75 insertions(+), 62 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v8 1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool
2026-06-09 2:59 [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Jianping Li
@ 2026-06-09 2:59 ` Jianping Li
2026-06-09 3:14 ` sashiko-bot
2026-06-09 2:59 ` [PATCH v8 2/4] misc: fastrpc: Remove buffer from list prior to unmap operation Jianping Li
` (3 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Jianping Li @ 2026-06-09 2:59 UTC (permalink / raw)
To: Srinivas Kandagatla, Amol Maheshwari
Cc: Ekansh Gupta, Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa,
Jorge Ramirez-Ortiz, linux-arm-msm, dri-devel, linux-kernel,
quic_chennak, stable, Dmitry Baryshkov, Jianping Li
From: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
The initial buffer allocated for the Audio PD memory pool is never added
to the pool because pageslen is set to 0. As a result, the buffer is not
registered with Audio PD and is never used, causing a memory leak. Audio
PD immediately falls back to allocating memory from the remote heap since
the pool starts out empty.
Fix this by setting pageslen to 1 so that the initially allocated buffer
is correctly registered and becomes part of the Audio PD memory pool.
Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd")
Cc: stable@kernel.org
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
---
drivers/misc/fastrpc.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index a9b2ae44c06f..96961217b856 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -1370,7 +1370,9 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
err = PTR_ERR(name);
goto err;
}
-
+ inbuf.client_id = fl->client_id;
+ inbuf.namelen = init.namelen;
+ inbuf.pageslen = 0;
if (!fl->cctx->remote_heap) {
err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen,
&fl->cctx->remote_heap);
@@ -1393,12 +1395,10 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
goto err_map;
}
scm_done = true;
+ inbuf.pageslen = 1;
}
}
- inbuf.client_id = fl->client_id;
- inbuf.namelen = init.namelen;
- inbuf.pageslen = 0;
fl->pd = USER_PD;
args[0].ptr = (u64)(uintptr_t)&inbuf;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v8 2/4] misc: fastrpc: Remove buffer from list prior to unmap operation
2026-06-09 2:59 [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Jianping Li
2026-06-09 2:59 ` [PATCH v8 1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool Jianping Li
@ 2026-06-09 2:59 ` Jianping Li
2026-06-09 3:09 ` sashiko-bot
2026-06-09 2:59 ` [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Jianping Li
` (2 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Jianping Li @ 2026-06-09 2:59 UTC (permalink / raw)
To: Srinivas Kandagatla, Amol Maheshwari
Cc: Ekansh Gupta, Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa,
Jorge Ramirez-Ortiz, linux-arm-msm, dri-devel, linux-kernel,
quic_chennak, stable, Dmitry Baryshkov, Jianping Li
From: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
fastrpc_req_munmap_impl() is called to unmap any buffer. The buffer is
getting removed from the list after it is unmapped from DSP. This can
create potential race conditions if multiple threads invoke unmap
concurrently, where one thread may remove the entry from the list while
another thread's unmap operation is still ongoing.
Fix this by removing the buffer entry from the list before calling the
unmap operation. If the unmap fails, the entry is re-added to the list
so that userspace can retry the unmap, or alternatively, the buffer
will be cleaned up during device release when the DSP process is torn
down and all DSP-side mappings are freed along with remaining buffers
in the list.
Fixes: 2419e55e532de ("misc: fastrpc: add mmap/unmap support")
Cc: stable@kernel.org
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
---
drivers/misc/fastrpc.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 96961217b856..517884000331 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -1889,9 +1889,6 @@ static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_buf *
&args[0]);
if (!err) {
dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr);
- spin_lock(&fl->lock);
- list_del(&buf->node);
- spin_unlock(&fl->lock);
fastrpc_buf_free(buf);
} else {
dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr);
@@ -1905,6 +1902,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
struct fastrpc_buf *buf = NULL, *iter, *b;
struct fastrpc_req_munmap req;
struct device *dev = fl->sctx->dev;
+ int err;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
@@ -1912,6 +1910,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
spin_lock(&fl->lock);
list_for_each_entry_safe(iter, b, &fl->mmaps, node) {
if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) {
+ list_del(&iter->node);
buf = iter;
break;
}
@@ -1924,7 +1923,14 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
return -EINVAL;
}
- return fastrpc_req_munmap_impl(fl, buf);
+ err = fastrpc_req_munmap_impl(fl, buf);
+ if (err) {
+ spin_lock(&fl->lock);
+ list_add_tail(&buf->node, &fl->mmaps);
+ spin_unlock(&fl->lock);
+ }
+
+ return err;
}
static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-06-09 2:59 [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Jianping Li
2026-06-09 2:59 ` [PATCH v8 1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool Jianping Li
2026-06-09 2:59 ` [PATCH v8 2/4] misc: fastrpc: Remove buffer from list prior to unmap operation Jianping Li
@ 2026-06-09 2:59 ` Jianping Li
2026-06-09 3:15 ` sashiko-bot
2026-07-01 20:37 ` Srinivas Kandagatla
2026-06-09 2:59 ` [PATCH v8 4/4] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL Jianping Li
2026-07-01 20:39 ` (subset) [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Srinivas Kandagatla
4 siblings, 2 replies; 15+ messages in thread
From: Jianping Li @ 2026-06-09 2:59 UTC (permalink / raw)
To: Srinivas Kandagatla, Amol Maheshwari
Cc: Jianping Li, Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa,
Jorge Ramirez-Ortiz, linux-arm-msm, dri-devel, linux-kernel,
ekansh.gupta, quic_chennak, stable
Allocating and freeing Audio PD memory from userspace is unsafe because
the kernel cannot reliably determine when the DSP has finished using the
memory. Userspace may free buffers while they are still in use by the DSP,
and remote free requests cannot be safely trusted.
Additionally, the current implementation allows userspace to repeatedly
grow the Audio PD heap, but does not support shrinking it. This can lead
to unbounded memory usage over time, effectively causing a memory leak.
Fix this by allocating the entire Audio PD reserved-memory region during
rpmsg probe and tying its lifetime to the rpmsg channel. This removes
userspace-controlled alloc/free and ensures that memory is reclaimed only
when the DSP process is torn down.
Add explicit validation for remote_heap presence and size before sending
the memory to DSP, and fail early if the reserved-memory region is
missing or incomplete.
Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd")
Cc: stable@kernel.org
Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
---
drivers/misc/fastrpc.c | 112 ++++++++++++++++++++++-------------------
1 file changed, 59 insertions(+), 53 deletions(-)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 517884000331..1942e74535e5 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -276,6 +276,8 @@ struct fastrpc_channel_ctx {
struct kref refcount;
/* Flag if dsp attributes are cached */
bool valid_attributes;
+ /* Flag if audio PD init mem was allocated */
+ bool audio_init_mem;
u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES];
struct fastrpc_device *secure_fdevice;
struct fastrpc_device *fdevice;
@@ -1341,15 +1343,24 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
struct fastrpc_init_create_static init;
struct fastrpc_invoke_args *args;
struct fastrpc_phy_page pages[1];
+ struct fastrpc_channel_ctx *cctx = fl->cctx;
char *name;
int err;
- bool scm_done = false;
struct {
int client_id;
u32 namelen;
u32 pageslen;
} inbuf;
u32 sc;
+ unsigned long flags;
+
+ if (!cctx->remote_heap || !cctx->remote_heap->dma_addr ||
+ !cctx->remote_heap->size) {
+ err = -ENOMEM;
+ dev_err(fl->sctx->dev,
+ "remote heap memory region is not added\n");
+ return err;
+ }
args = kzalloc_objs(*args, FASTRPC_CREATE_STATIC_PROCESS_NARGS);
if (!args)
@@ -1373,31 +1384,6 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
inbuf.client_id = fl->client_id;
inbuf.namelen = init.namelen;
inbuf.pageslen = 0;
- if (!fl->cctx->remote_heap) {
- err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen,
- &fl->cctx->remote_heap);
- if (err)
- goto err_name;
-
- /* Map if we have any heap VMIDs associated with this ADSP Static Process. */
- if (fl->cctx->vmcount) {
- u64 src_perms = BIT(QCOM_SCM_VMID_HLOS);
-
- err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
- (u64)fl->cctx->remote_heap->size,
- &src_perms,
- fl->cctx->vmperms, fl->cctx->vmcount);
- if (err) {
- dev_err(fl->sctx->dev,
- "Failed to assign memory with dma_addr %pad size 0x%llx err %d\n",
- &fl->cctx->remote_heap->dma_addr,
- fl->cctx->remote_heap->size, err);
- goto err_map;
- }
- scm_done = true;
- inbuf.pageslen = 1;
- }
- }
fl->pd = USER_PD;
@@ -1409,8 +1395,17 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
args[1].length = inbuf.namelen;
args[1].fd = -1;
- pages[0].addr = fl->cctx->remote_heap->dma_addr;
- pages[0].size = fl->cctx->remote_heap->size;
+ spin_lock_irqsave(&cctx->lock, flags);
+ if (!cctx->audio_init_mem) {
+ pages[0].addr = cctx->remote_heap->dma_addr;
+ pages[0].size = cctx->remote_heap->size;
+ cctx->audio_init_mem = true;
+ inbuf.pageslen = 1;
+ } else {
+ pages[0].addr = 0;
+ pages[0].size = 0;
+ }
+ spin_unlock_irqrestore(&cctx->lock, flags);
args[2].ptr = (u64)(uintptr_t) pages;
args[2].length = sizeof(*pages);
@@ -1428,27 +1423,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
return 0;
err_invoke:
- if (fl->cctx->vmcount && scm_done) {
- u64 src_perms = 0;
- struct qcom_scm_vmperm dst_perms;
- u32 i;
-
- for (i = 0; i < fl->cctx->vmcount; i++)
- src_perms |= BIT(fl->cctx->vmperms[i].vmid);
-
- dst_perms.vmid = QCOM_SCM_VMID_HLOS;
- dst_perms.perm = QCOM_SCM_PERM_RWX;
- err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
- (u64)fl->cctx->remote_heap->size,
- &src_perms, &dst_perms, 1);
- if (err)
- dev_err(fl->sctx->dev, "Failed to assign memory dma_addr %pad size 0x%llx err %d\n",
- &fl->cctx->remote_heap->dma_addr, fl->cctx->remote_heap->size, err);
- }
-err_map:
- fastrpc_buf_free(fl->cctx->remote_heap);
- fl->cctx->remote_heap = NULL;
-err_name:
+ cctx->audio_init_mem = false;
kfree(name);
err:
kfree(args);
@@ -2415,12 +2390,23 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
}
}
- if (domain_id == SDSP_DOMAIN_ID) {
+ if (domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID) {
struct resource res;
u64 src_perms;
err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res);
if (!err) {
+ if (domain_id == ADSP_DOMAIN_ID) {
+ data->remote_heap =
+ kzalloc_obj(*data->remote_heap, GFP_KERNEL);
+ if (!data->remote_heap) {
+ err = -ENOMEM;
+ goto err_free_data;
+ }
+
+ data->remote_heap->dma_addr = res.start;
+ data->remote_heap->size = resource_size(&res);
+ }
src_perms = BIT(QCOM_SCM_VMID_HLOS);
err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
@@ -2428,7 +2414,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
if (err)
goto err_free_data;
}
-
}
secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
@@ -2487,6 +2472,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
misc_deregister(&data->secure_fdevice->miscdev);
err_free_data:
+ kfree(data->remote_heap);
kfree(data);
return err;
}
@@ -2509,6 +2495,7 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
struct fastrpc_buf *buf, *b;
struct fastrpc_user *user;
unsigned long flags;
+ int err;
/* No invocations past this point */
spin_lock_irqsave(&cctx->lock, flags);
@@ -2526,8 +2513,27 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node)
list_del(&buf->node);
- if (cctx->remote_heap)
- fastrpc_buf_free(cctx->remote_heap);
+ if (cctx->remote_heap && cctx->vmcount) {
+ u64 src_perms = 0;
+ struct qcom_scm_vmperm dst_perms;
+
+ for (u32 i = 0; i < cctx->vmcount; i++)
+ src_perms |= BIT(cctx->vmperms[i].vmid);
+
+ dst_perms.vmid = QCOM_SCM_VMID_HLOS;
+ dst_perms.perm = QCOM_SCM_PERM_RWX;
+
+ err = qcom_scm_assign_mem(cctx->remote_heap->dma_addr,
+ cctx->remote_heap->size, &src_perms,
+ &dst_perms, 1);
+ if (err)
+ dev_err(&rpdev->dev,
+ "Failed to assign memory back to HLOS: dma_addr %pad size %#llx err %d\n",
+ &cctx->remote_heap->dma_addr, cctx->remote_heap->size, err);
+ }
+
+ kfree(cctx->remote_heap);
+ cctx->remote_heap = NULL;
of_platform_depopulate(&rpdev->dev);
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v8 4/4] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL
2026-06-09 2:59 [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Jianping Li
` (2 preceding siblings ...)
2026-06-09 2:59 ` [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Jianping Li
@ 2026-06-09 2:59 ` Jianping Li
2026-06-09 3:18 ` sashiko-bot
2026-07-01 20:39 ` (subset) [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Srinivas Kandagatla
4 siblings, 1 reply; 15+ messages in thread
From: Jianping Li @ 2026-06-09 2:59 UTC (permalink / raw)
To: Srinivas Kandagatla, Amol Maheshwari
Cc: Ekansh Gupta, Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa,
Jorge Ramirez-Ortiz, linux-arm-msm, dri-devel, linux-kernel,
quic_chennak, Dmitry Baryshkov, Jianping Li
From: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
Make fastrpc_buf_free() a no-op when passed a NULL pointer, allowing
callers to avoid open-coded NULL checks.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
---
drivers/misc/fastrpc.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 1942e74535e5..b4912b443b98 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -424,6 +424,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
static void fastrpc_buf_free(struct fastrpc_buf *buf)
{
+ if (!buf)
+ return;
+
dma_free_coherent(buf->dev, buf->size, buf->virt,
fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr));
kfree(buf);
@@ -516,8 +519,7 @@ static void fastrpc_user_free(struct kref *ref)
struct fastrpc_map *map, *m;
struct fastrpc_buf *buf, *b;
- if (fl->init_mem)
- fastrpc_buf_free(fl->init_mem);
+ fastrpc_buf_free(fl->init_mem);
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
list_del(&ctx->node);
@@ -562,8 +564,7 @@ static void fastrpc_context_free(struct kref *ref)
for (i = 0; i < ctx->nbufs; i++)
fastrpc_map_put(ctx->maps[i]);
- if (ctx->buf)
- fastrpc_buf_free(ctx->buf);
+ fastrpc_buf_free(ctx->buf);
spin_lock_irqsave(&cctx->lock, flags);
idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4);
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v8 2/4] misc: fastrpc: Remove buffer from list prior to unmap operation
2026-06-09 2:59 ` [PATCH v8 2/4] misc: fastrpc: Remove buffer from list prior to unmap operation Jianping Li
@ 2026-06-09 3:09 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-06-09 3:09 UTC (permalink / raw)
To: Jianping Li; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Critical] The patch introduces a critical use-after-free and list corruption vulnerability in the fastrpc_req_mmap() error path.
Pre-existing issues:
- [High] Concurrent accesses to fl->mmaps without holding fl->lock when handling -ERESTARTSYS.
--
commit a53abf279a47f6742d5cb44fb5a332592c4787b8
Author: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
misc: fastrpc: Remove buffer from list prior to unmap operation
This commit removes the buffer from the fastrpc user's mapped list before
calling the unmap operation to prevent concurrent unmap races. If the
unmap operation fails, the buffer is re-added to the list.
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index 96961217b8569..517884000331b 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
> @@ -1889,9 +1889,6 @@ static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_buf *
> &args[0]);
> if (!err) {
> dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr);
> - spin_lock(&fl->lock);
> - list_del(&buf->node);
> - spin_unlock(&fl->lock);
[Severity: Critical]
Does removing this list_del() introduce a use-after-free in the error
path of fastrpc_req_mmap()?
When fastrpc_req_mmap() encounters a copy_to_user() failure after successfully
adding the buffer to the fl->mmaps list, it jumps to the err_assign label
and calls fastrpc_req_munmap_impl() without first unlinking the buffer:
drivers/misc/fastrpc.c:fastrpc_req_mmap() {
...
spin_lock(&fl->lock);
list_add_tail(&buf->node, &fl->mmaps);
spin_unlock(&fl->lock);
if (copy_to_user((void __user *)argp, &req, sizeof(req))) {
err = -EFAULT;
goto err_assign;
}
...
err_assign:
fastrpc_req_munmap_impl(fl, buf);
return err;
}
Since fastrpc_req_munmap_impl() no longer removes the buffer from fl->mmaps
before calling fastrpc_buf_free(), the freed buffer will remain in the
list. Subsequent traverses of fl->mmaps will trigger a use-after-free.
> fastrpc_buf_free(buf);
> } else {
> dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr);
[ ... ]
> @@ -1924,7 +1923,14 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
> return -EINVAL;
> }
>
> - return fastrpc_req_munmap_impl(fl, buf);
> + err = fastrpc_req_munmap_impl(fl, buf);
[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the mmap
list handling here, I noticed concurrent modifications to fl->mmaps happen
locklessly in fastrpc_internal_invoke() when handling -ERESTARTSYS:
drivers/misc/fastrpc.c:fastrpc_internal_invoke() {
...
if (err == -ERESTARTSYS) {
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
list_del(&buf->node);
list_add_tail(&buf->node,
&fl->cctx->invoke_interrupted_mmaps);
}
}
...
}
Can this lead to list corruption if another thread concurrently executes
fastrpc_req_mmap() or fastrpc_req_munmap() and modifies fl->mmaps with
fl->lock held?
> + if (err) {
> + spin_lock(&fl->lock);
> + list_add_tail(&buf->node, &fl->mmaps);
> + spin_unlock(&fl->lock);
> + }
> +
> + return err;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609025938.457-1-jianping.li@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool
2026-06-09 2:59 ` [PATCH v8 1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool Jianping Li
@ 2026-06-09 3:14 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-06-09 3:14 UTC (permalink / raw)
To: Jianping Li; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] `fastrpc_map_attach()` doubly detaches and frees `map->buf` when error checks fail after mapping.
- [High] Pending invocation contexts and associated DMA mappings are permanently leaked if the remote DSP crashes.
--
commit c836369bdd9f243155871c03517e477035418fb9
Author: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
misc: fastrpc: Fix initial memory allocation for Audio PD memory pool
This commit fixes an issue where the initial buffer allocated for the
Audio PD memory pool was not registered because pageslen was 0. It updates
pageslen to 1 to ensure the buffer is correctly added to the pool.
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index a9b2ae44c06f6..96961217b8569 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
[ ... ]
[Severity: High]
This is a pre-existing issue, but does fastrpc_map_attach() doubly detach
and free map->buf when error checks fail after mapping?
If an error occurs after map->table is successfully assigned, the error
handling block runs:
fastrpc_map_attach() {
...
map_err:
dma_buf_detach(map->buf, map->attach);
attach_err:
dma_buf_put(map->buf);
get_err:
fastrpc_map_put(map);
}
Since fastrpc_map_put() invokes fastrpc_free_map(), which also calls
dma_buf_detach() and dma_buf_put() if map->table is set, does this cause a
double free and reference count underflow?
[Severity: High]
This is a pre-existing issue, but does fastrpc_rpmsg_remove() permanently
leak pending invocation contexts and associated DMA mappings if the remote
DSP crashes?
When fastrpc_invoke_send() sends a message to the DSP, it takes a reference
on the context via fastrpc_context_get(ctx) to represent DSP ownership. If
the DSP crashes, fastrpc_rpmsg_remove() runs:
fastrpc_rpmsg_remove() {
...
list_for_each_entry(user, &cctx->users, user)
fastrpc_notify_users(user);
...
}
Since the rpmsg channel is removed, the callback will never fire. Does
fastrpc_notify_users() wake up waiting threads without dropping the DSP's
reference count, preventing the ctx reference count from ever reaching zero?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609025938.457-1-jianping.li@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-06-09 2:59 ` [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Jianping Li
@ 2026-06-09 3:15 ` sashiko-bot
2026-07-01 20:37 ` Srinivas Kandagatla
1 sibling, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-06-09 3:15 UTC (permalink / raw)
To: Jianping Li; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] Unconditional call to `qcom_scm_assign_mem` during probe can result in an invalid TrustZone call or memory lockout if `data->vmcount` is 0.
- [High] Unconditional and lockless clearance of `cctx->audio_init_mem` on the static process error path corrupts initialization synchronization.
- [High] Removal of the dynamic heap fallback breaks Audio PD on older Device Trees.
- [High] Missing rollback of `qcom_scm_assign_mem` on probe failure permanently locks out SCM memory.
Pre-existing issues:
- [High] Time-of-check to time-of-use (TOCTOU) race condition leading to a NULL pointer dereference of `cctx->remote_heap`.
--
commit 780b6a562518105022f75b2b13f38c9dbad812e3
Author: Jianping Li <jianping.li@oss.qualcomm.com>
misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
This patch changes the Audio PD memory allocation to use the entire
reserved-memory region during rpmsg probe, tying its lifetime to the rpmsg
channel. It also adds explicit validation for the remote heap before sending
memory to the DSP.
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index 517884000331b..1942e74535e52 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
[ ... ]
> @@ -1341,15 +1343,24 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
> struct fastrpc_init_create_static init;
> struct fastrpc_invoke_args *args;
> struct fastrpc_phy_page pages[1];
> + struct fastrpc_channel_ctx *cctx = fl->cctx;
> char *name;
> int err;
> struct {
> int client_id;
> u32 namelen;
> u32 pageslen;
> } inbuf;
> u32 sc;
> + unsigned long flags;
> +
> + if (!cctx->remote_heap || !cctx->remote_heap->dma_addr ||
> + !cctx->remote_heap->size) {
> + err = -ENOMEM;
> + dev_err(fl->sctx->dev,
> + "remote heap memory region is not added\n");
> + return err;
> + }
[Severity: High]
Does the removal of the dynamic heap fallback break backwards compatibility?
In the original code, if the device tree lacked a memory-region, the heap was
dynamically allocated via fastrpc_remote_heap_alloc(). Now, platforms with
older device trees will see FASTRPC_IOCTL_INIT_CREATE_STATIC fail with
-ENOMEM.
[ ... ]
> @@ -1409,8 +1395,17 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
> args[1].length = inbuf.namelen;
> args[1].fd = -1;
>
> - pages[0].addr = fl->cctx->remote_heap->dma_addr;
> - pages[0].size = fl->cctx->remote_heap->size;
> + spin_lock_irqsave(&cctx->lock, flags);
> + if (!cctx->audio_init_mem) {
> + pages[0].addr = cctx->remote_heap->dma_addr;
> + pages[0].size = cctx->remote_heap->size;
[Severity: High]
This is a pre-existing issue, but can this lead to a NULL pointer
dereference?
In fastrpc_init_create_static_process(), cctx->remote_heap is checked
locklessly at the start of the function. If a concurrent
fastrpc_rpmsg_remove() executes during a sleeping operation like
copy_from_user():
CPU1
fastrpc_rpmsg_remove()
...
kfree(cctx->remote_heap);
cctx->remote_heap = NULL;
Will CPU2's unconditional dereference of cctx->remote_heap->dma_addr inside
cctx->lock cause a kernel panic?
> + cctx->audio_init_mem = true;
> + inbuf.pageslen = 1;
> + } else {
> + pages[0].addr = 0;
> + pages[0].size = 0;
> + }
> + spin_unlock_irqrestore(&cctx->lock, flags);
[ ... ]
> @@ -1428,27 +1423,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>
> return 0;
> err_invoke:
> - if (fl->cctx->vmcount && scm_done) {
> - u64 src_perms = 0;
> - struct qcom_scm_vmperm dst_perms;
> - u32 i;
> -
> - for (i = 0; i < fl->cctx->vmcount; i++)
> - src_perms |= BIT(fl->cctx->vmperms[i].vmid);
> -
> - dst_perms.vmid = QCOM_SCM_VMID_HLOS;
> - dst_perms.perm = QCOM_SCM_PERM_RWX;
> - err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
> - (u64)fl->cctx->remote_heap->size,
> - &src_perms, &dst_perms, 1);
> - if (err)
> - dev_err(fl->sctx->dev, "Failed to assign memory dma_addr %pad size 0x%llx err %d\n",
> - &fl->cctx->remote_heap->dma_addr, fl->cctx->remote_heap->size, err);
> - }
> -err_map:
> - fastrpc_buf_free(fl->cctx->remote_heap);
> - fl->cctx->remote_heap = NULL;
> -err_name:
> + cctx->audio_init_mem = false;
[Severity: High]
Can this lockless assignment cause a data race that corrupts initialization
synchronization?
If Thread A successfully sets cctx->audio_init_mem = true inside cctx->lock
and proceeds, and Thread B concurrently executes the ioctl, skips
initialization because the flag is true, but then fails its invoke operation,
Thread B will unconditionally clear the flag for the entire channel. Does
this break the single-initialization guarantee?
> kfree(name);
> err:
> kfree(args);
[ ... ]
> @@ -2415,12 +2390,23 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
> }
> }
>
> - if (domain_id == SDSP_DOMAIN_ID) {
> + if (domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID) {
> struct resource res;
> u64 src_perms;
>
> err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res);
> if (!err) {
> + if (domain_id == ADSP_DOMAIN_ID) {
> + data->remote_heap =
> + kzalloc_obj(*data->remote_heap, GFP_KERNEL);
> + if (!data->remote_heap) {
> + err = -ENOMEM;
> + goto err_free_data;
> + }
> +
> + data->remote_heap->dma_addr = res.start;
> + data->remote_heap->size = resource_size(&res);
> + }
> src_perms = BIT(QCOM_SCM_VMID_HLOS);
>
> err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
> @@ -2428,7 +2414,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
> if (err)
> goto err_free_data;
[Severity: High]
Is it safe to call qcom_scm_assign_mem() here without verifying
data->vmcount > 0?
If the device tree lacks the qcom,vmids property, data->vmcount is 0. Passing
a dest_cnt of 0 to TrustZone could cause the SMC call to fail or permanently
lock out the memory by stripping HLOS permissions, especially since
fastrpc_rpmsg_remove() skips restoring memory if cctx->vmcount == 0.
> }
> }
[ ... ]
> @@ -2487,6 +2472,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
> misc_deregister(&data->secure_fdevice->miscdev);
>
> err_free_data:
> + kfree(data->remote_heap);
> kfree(data);
> return err;
> }
[Severity: High]
Is there a missing rollback for qcom_scm_assign_mem() on this error path?
If the initial assignment succeeds, but a subsequent probe step like
fastrpc_device_register() fails, the driver memory is freed but the
hypervisor mapping is not restored to QCOM_SCM_VMID_HLOS. Can this
permanently lock out the SCM memory and cause subsequent probes to fail?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609025938.457-1-jianping.li@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 4/4] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL
2026-06-09 2:59 ` [PATCH v8 4/4] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL Jianping Li
@ 2026-06-09 3:18 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-06-09 3:18 UTC (permalink / raw)
To: Jianping Li; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Exported DMA buffers outlive their fastrpc_user context, leading to a Use-After-Free on dmabuf operations.
- [Critical] In `fastrpc_req_mmap()`, if `copy_to_user()` fails, the allocated buffer is freed via `fastrpc_req_munmap_impl()`, but its node is never removed from `fl->mmaps`.
- [Critical] Timeouts in kernel RPCs cause DMA buffers to be freed while the DSP is still actively using them.
- [High] Interrupted mmaps are unlinked but their memory is never freed during `fastrpc_rpmsg_remove()`.
- [High] Invocation contexts leak references to attached maps for handles, allowing unbounded resource accumulation.
- [High] Failure to restore hypervisor memory permissions in `fastrpc_req_munmap_impl` for remote heap allocations.
--
commit c345df439255945e9c8c222e251d798521775ff6
Author: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
misc: fastrpc: Allow fastrpc_buf_free() to accept NULL
Make fastrpc_buf_free() a no-op when passed a NULL pointer, allowing
callers to avoid open-coded NULL checks.
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index 1942e74535e52..b4912b443b982 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
> @@ -424,6 +424,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
>
> static void fastrpc_buf_free(struct fastrpc_buf *buf)
> {
> + if (!buf)
> + return;
> +
> dma_free_coherent(buf->dev, buf->size, buf->virt,
> fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr));
[Severity: Critical]
This is a pre-existing issue, but does fastrpc_buf_free() have a
use-after-free risk when called from the dmabuf release path?
If a user closes the fastrpc device descriptor but keeps the DMA-BUF file
descriptor open, the fastrpc_user (fl) context is freed. When the DMA-BUF
is later released, it calls fastrpc_buf_free(), which dereferences
buf->fl->cctx here to calculate the DMA address.
Since fastrpc_dmabuf_alloc() doesn't take a reference on the fastrpc_user
when exporting the DMA-BUF, can this lead to memory corruption?
[Severity: High]
This isn't a bug introduced by this patch, but does fastrpc_buf_free()
safely handle buffers mapped with ADSP_MMAP_REMOTE_HEAP_ADDR?
When fastrpc_req_mmap() maps a remote heap buffer, it uses
qcom_scm_assign_mem() to grant access to the DSP VM. During unmapping,
fastrpc_req_munmap_impl() calls fastrpc_buf_free() directly.
Will handing this restricted memory back to the kernel's allocator without
first calling qcom_scm_assign_mem() to restore ownership to the HLOS lead
to a hypervisor fault when the kernel tries to use the memory?
[Severity: Critical]
This is a pre-existing issue, but does calling fastrpc_buf_free() on
timeout lead to memory corruption by the DSP?
When fastrpc_internal_invoke() times out, callers like fastrpc_req_mmap()
and fastrpc_init_create_process() treat it as a failure and immediately
call fastrpc_buf_free(buf) to release the kernel memory.
Since there is no mechanism to cancel the pending request on the DSP, won't
the DSP continue processing and access the freed DMA memory, potentially
corrupting memory that has been reallocated by the system?
> kfree(buf);
> }
> @@ -516,8 +519,7 @@ static void fastrpc_user_free(struct kref *ref)
> struct fastrpc_map *map, *m;
> struct fastrpc_buf *buf, *b;
>
> - if (fl->init_mem)
> - fastrpc_buf_free(fl->init_mem);
> + fastrpc_buf_free(fl->init_mem);
>
> list_for_each_entry_safe(ctx, n, &fl->pending, node) {
[Severity: Critical]
This isn't a bug introduced by this patch, but does fastrpc_user_free()
risk a use-after-free and double free when cleaning up fl->mmaps?
In fastrpc_req_mmap(), if copy_to_user() fails, the code jumps to
err_assign and calls fastrpc_req_munmap_impl(). This unmaps and frees the
buffer using fastrpc_buf_free(), but leaves its node in the fl->mmaps
list.
When fastrpc_user_free() later loops over fl->mmaps, will it attempt to
access and free this already-freed buffer?
[Severity: High]
This is a pre-existing issue, but does fastrpc_rpmsg_remove() leak
interrupted mmaps when tearing down the channel?
When an invocation returns -ERESTARTSYS, mapped buffers are moved from
fl->mmaps to cctx->invoke_interrupted_mmaps. During fastrpc_rpmsg_remove(),
the elements are unlinked:
list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node)
list_del(&buf->node);
Does skipping the fastrpc_buf_free(buf) call here cause a permanent leak
of the fastrpc_buf and its associated DMA/SMMU memory allocations?
> list_del(&ctx->node);
> @@ -562,8 +564,7 @@ static void fastrpc_context_free(struct kref *ref)
> for (i = 0; i < ctx->nbufs; i++)
> fastrpc_map_put(ctx->maps[i]);
>
[Severity: High]
This isn't a bug introduced by this patch, but does fastrpc_context_free()
leak references to attached maps for handles?
In fastrpc_create_maps(), the ctx->maps array is populated for all
nscalars. For indices >= nbufs (handles), fastrpc_map_attach() creates
a new map and adds it to fl->maps.
Since this cleanup loop only iterates up to ctx->nbufs, does it fail to
drop the references for the remaining handle maps in ctx->maps, causing
unbounded accumulation in fl->maps?
> - if (ctx->buf)
> - fastrpc_buf_free(ctx->buf);
> + fastrpc_buf_free(ctx->buf);
>
> spin_lock_irqsave(&cctx->lock, flags);
> idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609025938.457-1-jianping.li@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-06-09 2:59 ` [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Jianping Li
2026-06-09 3:15 ` sashiko-bot
@ 2026-07-01 20:37 ` Srinivas Kandagatla
2026-07-02 4:02 ` Ekansh Gupta
2026-07-03 4:45 ` Jianping Li
1 sibling, 2 replies; 15+ messages in thread
From: Srinivas Kandagatla @ 2026-07-01 20:37 UTC (permalink / raw)
To: Jianping Li, Srinivas Kandagatla, Amol Maheshwari
Cc: Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa, Jorge Ramirez-Ortiz,
linux-arm-msm, dri-devel, linux-kernel, ekansh.gupta,
quic_chennak, stable
On 6/9/26 3:59 AM, Jianping Li wrote:
> Allocating and freeing Audio PD memory from userspace is unsafe because
> the kernel cannot reliably determine when the DSP has finished using the
> memory. Userspace may free buffers while they are still in use by the DSP,
> and remote free requests cannot be safely trusted.
>
> Additionally, the current implementation allows userspace to repeatedly
> grow the Audio PD heap, but does not support shrinking it. This can lead
> to unbounded memory usage over time, effectively causing a memory leak.
>
> Fix this by allocating the entire Audio PD reserved-memory region during
> rpmsg probe and tying its lifetime to the rpmsg channel. This removes
> userspace-controlled alloc/free and ensures that memory is reclaimed only
> when the DSP process is torn down.
>
> Add explicit validation for remote_heap presence and size before sending
> the memory to DSP, and fail early if the reserved-memory region is
> missing or incomplete.
>
> Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd")
> Cc: stable@kernel.org
> Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
> ---
> drivers/misc/fastrpc.c | 112 ++++++++++++++++++++++-------------------
> 1 file changed, 59 insertions(+), 53 deletions(-)
>
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index 517884000331..1942e74535e5 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
> @@ -276,6 +276,8 @@ struct fastrpc_channel_ctx {
> struct kref refcount;
> /* Flag if dsp attributes are cached */
> bool valid_attributes;
> + /* Flag if audio PD init mem was allocated */
> + bool audio_init_mem;
> u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES];
> struct fastrpc_device *secure_fdevice;
> struct fastrpc_device *fdevice;
> @@ -1341,15 +1343,24 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
> struct fastrpc_init_create_static init;
> struct fastrpc_invoke_args *args;
> struct fastrpc_phy_page pages[1];
> + struct fastrpc_channel_ctx *cctx = fl->cctx;
> char *name;
> int err;
> - bool scm_done = false;
> struct {
> int client_id;
> u32 namelen;
> u32 pageslen;
> } inbuf;
> u32 sc;
> + unsigned long flags;
> +
> + if (!cctx->remote_heap || !cctx->remote_heap->dma_addr ||
> + !cctx->remote_heap->size) {
> + err = -ENOMEM;
> + dev_err(fl->sctx->dev,
> + "remote heap memory region is not added\n");
> + return err;
> + }
>
> args = kzalloc_objs(*args, FASTRPC_CREATE_STATIC_PROCESS_NARGS);
> if (!args)
> @@ -1373,31 +1384,6 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
> inbuf.client_id = fl->client_id;
> inbuf.namelen = init.namelen;
> inbuf.pageslen = 0;
> - if (!fl->cctx->remote_heap) {
> - err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen,
> - &fl->cctx->remote_heap);
> - if (err)
> - goto err_name;
> -
> - /* Map if we have any heap VMIDs associated with this ADSP Static Process. */
> - if (fl->cctx->vmcount) {
> - u64 src_perms = BIT(QCOM_SCM_VMID_HLOS);
> -
> - err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
> - (u64)fl->cctx->remote_heap->size,
> - &src_perms,
> - fl->cctx->vmperms, fl->cctx->vmcount);
> - if (err) {
> - dev_err(fl->sctx->dev,
> - "Failed to assign memory with dma_addr %pad size 0x%llx err %d\n",
> - &fl->cctx->remote_heap->dma_addr,
> - fl->cctx->remote_heap->size, err);
> - goto err_map;
> - }
> - scm_done = true;
> - inbuf.pageslen = 1;
> - }
> - }
>
> fl->pd = USER_PD;
>
> @@ -1409,8 +1395,17 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
> args[1].length = inbuf.namelen;
> args[1].fd = -1;
>
> - pages[0].addr = fl->cctx->remote_heap->dma_addr;
> - pages[0].size = fl->cctx->remote_heap->size;
> + spin_lock_irqsave(&cctx->lock, flags);
> + if (!cctx->audio_init_mem) {
> + pages[0].addr = cctx->remote_heap->dma_addr;
> + pages[0].size = cctx->remote_heap->size;
> + cctx->audio_init_mem = true;
> + inbuf.pageslen = 1;
> + } else {
> + pages[0].addr = 0;
> + pages[0].size = 0;
What is the expected behavoiur in this case?
> + }
> + spin_unlock_irqrestore(&cctx->lock, flags);
>
> args[2].ptr = (u64)(uintptr_t) pages;
> args[2].length = sizeof(*pages);
> @@ -1428,27 +1423,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>
> return 0;
> err_invoke:
> - if (fl->cctx->vmcount && scm_done) {
> - u64 src_perms = 0;
> - struct qcom_scm_vmperm dst_perms;
> - u32 i;
> -
> - for (i = 0; i < fl->cctx->vmcount; i++)
> - src_perms |= BIT(fl->cctx->vmperms[i].vmid);
> -
> - dst_perms.vmid = QCOM_SCM_VMID_HLOS;
> - dst_perms.perm = QCOM_SCM_PERM_RWX;
> - err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
> - (u64)fl->cctx->remote_heap->size,
> - &src_perms, &dst_perms, 1);
> - if (err)
> - dev_err(fl->sctx->dev, "Failed to assign memory dma_addr %pad size 0x%llx err %d\n",
> - &fl->cctx->remote_heap->dma_addr, fl->cctx->remote_heap->size, err);
> - }
> -err_map:
> - fastrpc_buf_free(fl->cctx->remote_heap);
> - fl->cctx->remote_heap = NULL;
> -err_name:
> + cctx->audio_init_mem = false;
this can race.
> kfree(name);
> err:
> kfree(args);
> @@ -2415,12 +2390,23 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
> }
> }
>
> - if (domain_id == SDSP_DOMAIN_ID) {
> + if (domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID) {
> struct resource res;
> u64 src_perms;
>
> err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res);
> if (!err) {
> + if (domain_id == ADSP_DOMAIN_ID) {
> + data->remote_heap =
> + kzalloc_obj(*data->remote_heap, GFP_KERNEL);
> + if (!data->remote_heap) {
> + err = -ENOMEM;
> + goto err_free_data;
> + }
> +
> + data->remote_heap->dma_addr = res.start;
> + data->remote_heap->size = resource_size(&res);
> + }
> src_perms = BIT(QCOM_SCM_VMID_HLOS);
>
> err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
> @@ -2428,7 +2414,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
> if (err)
> goto err_free_data;
> }
> -
> }
>
> secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
> @@ -2487,6 +2472,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
> misc_deregister(&data->secure_fdevice->miscdev);
>
> err_free_data:
> + kfree(data->remote_heap);
> kfree(data);
> return err;
> }
> @@ -2509,6 +2495,7 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
> struct fastrpc_buf *buf, *b;
> struct fastrpc_user *user;
> unsigned long flags;
> + int err;
>
> /* No invocations past this point */
> spin_lock_irqsave(&cctx->lock, flags);
> @@ -2526,8 +2513,27 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
> list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node)
> list_del(&buf->node);
>
> - if (cctx->remote_heap)
> - fastrpc_buf_free(cctx->remote_heap);
> + if (cctx->remote_heap && cctx->vmcount) {
> + u64 src_perms = 0;
> + struct qcom_scm_vmperm dst_perms;
> +
> + for (u32 i = 0; i < cctx->vmcount; i++)
> + src_perms |= BIT(cctx->vmperms[i].vmid);
> +
> + dst_perms.vmid = QCOM_SCM_VMID_HLOS;
> + dst_perms.perm = QCOM_SCM_PERM_RWX;
> +
> + err = qcom_scm_assign_mem(cctx->remote_heap->dma_addr,
> + cctx->remote_heap->size, &src_perms,
> + &dst_perms, 1);
> + if (err)
> + dev_err(&rpdev->dev,
> + "Failed to assign memory back to HLOS: dma_addr %pad size %#llx err %d\n",
> + &cctx->remote_heap->dma_addr, cctx->remote_heap->size, err);
> + }
> +
> + kfree(cctx->remote_heap);
> + cctx->remote_heap = NULL;
>
> of_platform_depopulate(&rpdev->dev);
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: (subset) [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes
2026-06-09 2:59 [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Jianping Li
` (3 preceding siblings ...)
2026-06-09 2:59 ` [PATCH v8 4/4] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL Jianping Li
@ 2026-07-01 20:39 ` Srinivas Kandagatla
4 siblings, 0 replies; 15+ messages in thread
From: Srinivas Kandagatla @ 2026-07-01 20:39 UTC (permalink / raw)
To: Amol Maheshwari, Jianping Li
Cc: Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa, linux-arm-msm,
dri-devel, linux-kernel, ekansh.gupta, quic_chennak,
Jorge Ramirez-Ortiz
On Tue, 09 Jun 2026 10:59:34 +0800, Jianping Li wrote:
> Add missing bug fixes in memory areas. This patch series fixes multiple memory
> handling issues in the FastRPC driver, primarily around the Audio PD remote heap.
>
> The Audio PD uses a reserved memory-region that is shared between HLOS
> and the DSP. Allocating and freeing this memory from userspace is unsafe,
> as the kernel cannot reliably determine when the DSP has finished using
> the buffers.
>
> [...]
Applied, thanks!
[1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool
commit: 36090f35ca19c1422bd56f03cdc3b5703ca18a15
[2/4] misc: fastrpc: Remove buffer from list prior to unmap operation
commit: 902b8ec9dd1098cc133cffa8be0b6378544f27ba
[4/4] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL
commit: a4f06f06c20a72b2ee96fd964ce93a18e321bd0a
Best regards,
--
Srinivas Kandagatla <srini@kernel.org>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-07-01 20:37 ` Srinivas Kandagatla
@ 2026-07-02 4:02 ` Ekansh Gupta
2026-07-02 6:46 ` Srinivas Kandagatla
2026-07-03 4:45 ` Jianping Li
1 sibling, 1 reply; 15+ messages in thread
From: Ekansh Gupta @ 2026-07-02 4:02 UTC (permalink / raw)
To: Srinivas Kandagatla, Jianping Li, Amol Maheshwari
Cc: Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa, Jorge Ramirez-Ortiz,
linux-arm-msm, dri-devel, linux-kernel, quic_chennak, stable
On 02-07-2026 02:07, Srinivas Kandagatla wrote:
>
>
> On 6/9/26 3:59 AM, Jianping Li wrote:
>> Allocating and freeing Audio PD memory from userspace is unsafe because
>> the kernel cannot reliably determine when the DSP has finished using the
>> memory. Userspace may free buffers while they are still in use by the DSP,
>> and remote free requests cannot be safely trusted.
>>
>> Additionally, the current implementation allows userspace to repeatedly
>> grow the Audio PD heap, but does not support shrinking it. This can lead
>> to unbounded memory usage over time, effectively causing a memory leak.
>>
>> Fix this by allocating the entire Audio PD reserved-memory region during
>> rpmsg probe and tying its lifetime to the rpmsg channel. This removes
>> userspace-controlled alloc/free and ensures that memory is reclaimed only
>> when the DSP process is torn down.
>>
>> Add explicit validation for remote_heap presence and size before sending
>> the memory to DSP, and fail early if the reserved-memory region is
>> missing or incomplete.
>>
>> Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd")
>> Cc: stable@kernel.org
>> Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
>> ---
>> drivers/misc/fastrpc.c | 112 ++++++++++++++++++++++-------------------
>> 1 file changed, 59 insertions(+), 53 deletions(-)
>>
>> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
>> index 517884000331..1942e74535e5 100644
>> --- a/drivers/misc/fastrpc.c
>> +++ b/drivers/misc/fastrpc.c
>> @@ -276,6 +276,8 @@ struct fastrpc_channel_ctx {
>> struct kref refcount;
>> /* Flag if dsp attributes are cached */
>> bool valid_attributes;
>> + /* Flag if audio PD init mem was allocated */
>> + bool audio_init_mem;
>> u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES];
>> struct fastrpc_device *secure_fdevice;
>> struct fastrpc_device *fdevice;
>> @@ -1341,15 +1343,24 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>> struct fastrpc_init_create_static init;
>> struct fastrpc_invoke_args *args;
>> struct fastrpc_phy_page pages[1];
>> + struct fastrpc_channel_ctx *cctx = fl->cctx;
>> char *name;
>> int err;
>> - bool scm_done = false;
>> struct {
>> int client_id;
>> u32 namelen;
>> u32 pageslen;
>> } inbuf;
>> u32 sc;
>> + unsigned long flags;
>> +
>> + if (!cctx->remote_heap || !cctx->remote_heap->dma_addr ||
>> + !cctx->remote_heap->size) {
>> + err = -ENOMEM;
>> + dev_err(fl->sctx->dev,
>> + "remote heap memory region is not added\n");
>> + return err;
>> + }
>>
>> args = kzalloc_objs(*args, FASTRPC_CREATE_STATIC_PROCESS_NARGS);
>> if (!args)
>> @@ -1373,31 +1384,6 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>> inbuf.client_id = fl->client_id;
>> inbuf.namelen = init.namelen;
>> inbuf.pageslen = 0;
>> - if (!fl->cctx->remote_heap) {
>> - err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen,
>> - &fl->cctx->remote_heap);
>> - if (err)
>> - goto err_name;
>> -
>> - /* Map if we have any heap VMIDs associated with this ADSP Static Process. */
>> - if (fl->cctx->vmcount) {
>> - u64 src_perms = BIT(QCOM_SCM_VMID_HLOS);
>> -
>> - err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
>> - (u64)fl->cctx->remote_heap->size,
>> - &src_perms,
>> - fl->cctx->vmperms, fl->cctx->vmcount);
>> - if (err) {
>> - dev_err(fl->sctx->dev,
>> - "Failed to assign memory with dma_addr %pad size 0x%llx err %d\n",
>> - &fl->cctx->remote_heap->dma_addr,
>> - fl->cctx->remote_heap->size, err);
>> - goto err_map;
>> - }
>> - scm_done = true;
>> - inbuf.pageslen = 1;
>> - }
>> - }
>>
>> fl->pd = USER_PD;
>>
>> @@ -1409,8 +1395,17 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>> args[1].length = inbuf.namelen;
>> args[1].fd = -1;
>>
>> - pages[0].addr = fl->cctx->remote_heap->dma_addr;
>> - pages[0].size = fl->cctx->remote_heap->size;
>> + spin_lock_irqsave(&cctx->lock, flags);
>> + if (!cctx->audio_init_mem) {
>> + pages[0].addr = cctx->remote_heap->dma_addr;
>> + pages[0].size = cctx->remote_heap->size;
>> + cctx->audio_init_mem = true;
>> + inbuf.pageslen = 1;
>> + } else {
>> + pages[0].addr = 0;
>> + pages[0].size = 0;
>
> What is the expected behavoiur in this case?
Audio daemon is expected to take the memory information to DSP audio PD
only the first time it goes and attaches there.
This is the case where daemon was killed but audio PD session is still
running. In such cases, daemon is not expected to take any memory
information to audio PD as the earlier shared information is already
there with audio PD which it is using irrespective of daemon state.>
>> + }
>> + spin_unlock_irqrestore(&cctx->lock, flags);
>>
>> args[2].ptr = (u64)(uintptr_t) pages;
>> args[2].length = sizeof(*pages);
>> @@ -1428,27 +1423,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>>
>> return 0;
>> err_invoke:
>> - if (fl->cctx->vmcount && scm_done) {
>> - u64 src_perms = 0;
>> - struct qcom_scm_vmperm dst_perms;
>> - u32 i;
>> -
>> - for (i = 0; i < fl->cctx->vmcount; i++)
>> - src_perms |= BIT(fl->cctx->vmperms[i].vmid);
>> -
>> - dst_perms.vmid = QCOM_SCM_VMID_HLOS;
>> - dst_perms.perm = QCOM_SCM_PERM_RWX;
>> - err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
>> - (u64)fl->cctx->remote_heap->size,
>> - &src_perms, &dst_perms, 1);
>> - if (err)
>> - dev_err(fl->sctx->dev, "Failed to assign memory dma_addr %pad size 0x%llx err %d\n",
>> - &fl->cctx->remote_heap->dma_addr, fl->cctx->remote_heap->size, err);
>> - }
>> -err_map:
>> - fastrpc_buf_free(fl->cctx->remote_heap);
>> - fl->cctx->remote_heap = NULL;
>> -err_name:
>> + cctx->audio_init_mem = false;
> this can race.
>
>> kfree(name);
>> err:
>> kfree(args);
>> @@ -2415,12 +2390,23 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
>> }
>> }
>>
>> - if (domain_id == SDSP_DOMAIN_ID) {
>> + if (domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID) {
>> struct resource res;
>> u64 src_perms;
>>
>> err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res);
>> if (!err) {
>> + if (domain_id == ADSP_DOMAIN_ID) {
>> + data->remote_heap =
>> + kzalloc_obj(*data->remote_heap, GFP_KERNEL);
>> + if (!data->remote_heap) {
>> + err = -ENOMEM;
>> + goto err_free_data;
>> + }
>> +
>> + data->remote_heap->dma_addr = res.start;
>> + data->remote_heap->size = resource_size(&res);
>> + }
>> src_perms = BIT(QCOM_SCM_VMID_HLOS);
>>
>> err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
>> @@ -2428,7 +2414,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
>> if (err)
>> goto err_free_data;
>> }
>> -
>> }
>>
>> secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
>> @@ -2487,6 +2472,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
>> misc_deregister(&data->secure_fdevice->miscdev);
>>
>> err_free_data:
>> + kfree(data->remote_heap);
>> kfree(data);
>> return err;
>> }
>> @@ -2509,6 +2495,7 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
>> struct fastrpc_buf *buf, *b;
>> struct fastrpc_user *user;
>> unsigned long flags;
>> + int err;
>>
>> /* No invocations past this point */
>> spin_lock_irqsave(&cctx->lock, flags);
>> @@ -2526,8 +2513,27 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
>> list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node)
>> list_del(&buf->node);
>>
>> - if (cctx->remote_heap)
>> - fastrpc_buf_free(cctx->remote_heap);
>> + if (cctx->remote_heap && cctx->vmcount) {
>> + u64 src_perms = 0;
>> + struct qcom_scm_vmperm dst_perms;
>> +
>> + for (u32 i = 0; i < cctx->vmcount; i++)
>> + src_perms |= BIT(cctx->vmperms[i].vmid);
>> +
>> + dst_perms.vmid = QCOM_SCM_VMID_HLOS;
>> + dst_perms.perm = QCOM_SCM_PERM_RWX;
>> +
>> + err = qcom_scm_assign_mem(cctx->remote_heap->dma_addr,
>> + cctx->remote_heap->size, &src_perms,
>> + &dst_perms, 1);
>> + if (err)
>> + dev_err(&rpdev->dev,
>> + "Failed to assign memory back to HLOS: dma_addr %pad size %#llx err %d\n",
>> + &cctx->remote_heap->dma_addr, cctx->remote_heap->size, err);
>> + }
>> +
>> + kfree(cctx->remote_heap);
>> + cctx->remote_heap = NULL;
>>
>> of_platform_depopulate(&rpdev->dev);
>>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-07-02 4:02 ` Ekansh Gupta
@ 2026-07-02 6:46 ` Srinivas Kandagatla
2026-07-02 8:43 ` Ekansh Gupta
0 siblings, 1 reply; 15+ messages in thread
From: Srinivas Kandagatla @ 2026-07-02 6:46 UTC (permalink / raw)
To: Ekansh Gupta, Srinivas Kandagatla, Jianping Li, Amol Maheshwari
Cc: Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa, Jorge Ramirez-Ortiz,
linux-arm-msm, dri-devel, linux-kernel, quic_chennak, stable
On 7/2/26 5:02 AM, Ekansh Gupta wrote:
>>> @@ -1409,8 +1395,17 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>>> args[1].length = inbuf.namelen;
>>> args[1].fd = -1;
>>>
>>> - pages[0].addr = fl->cctx->remote_heap->dma_addr;
>>> - pages[0].size = fl->cctx->remote_heap->size;
>>> + spin_lock_irqsave(&cctx->lock, flags);
>>> + if (!cctx->audio_init_mem) {
>>> + pages[0].addr = cctx->remote_heap->dma_addr;
>>> + pages[0].size = cctx->remote_heap->size;
>>> + cctx->audio_init_mem = true;
>>> + inbuf.pageslen = 1;
>>> + } else {
>>> + pages[0].addr = 0;
>>> + pages[0].size = 0;
>> What is the expected behavoiur in this case?
> Audio daemon is expected to take the memory information to DSP audio PD
> only the first time it goes and attaches there.
>
> This is the case where daemon was killed but audio PD session is still
> running. In such cases, daemon is not expected to take any memory
> information to audio PD as the earlier shared information is already
> there with audio PD which it is using irrespective of daemon state.>
Wow, this behavior is not documented or pl consider adding a comment
here, Can we not query the dsp before creating new audiopd service?
Does it make sense to attach instead of creating?
--srini
>>> + }
>>> + spin_unlock_irqrestore(&cctx->lock, flags);
>>>
>>> args[2].ptr = (u64)(uintptr_t) pages;
>>> args[2].length = sizeof(*pages);
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-07-02 6:46 ` Srinivas Kandagatla
@ 2026-07-02 8:43 ` Ekansh Gupta
0 siblings, 0 replies; 15+ messages in thread
From: Ekansh Gupta @ 2026-07-02 8:43 UTC (permalink / raw)
To: Srinivas Kandagatla, Jianping Li, Amol Maheshwari
Cc: Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa, Jorge Ramirez-Ortiz,
linux-arm-msm, dri-devel, linux-kernel, quic_chennak, stable
On 02-07-2026 12:16, Srinivas Kandagatla wrote:
>
>
> On 7/2/26 5:02 AM, Ekansh Gupta wrote:
>>>> @@ -1409,8 +1395,17 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>>>> args[1].length = inbuf.namelen;
>>>> args[1].fd = -1;
>>>>
>>>> - pages[0].addr = fl->cctx->remote_heap->dma_addr;
>>>> - pages[0].size = fl->cctx->remote_heap->size;
>>>> + spin_lock_irqsave(&cctx->lock, flags);
>>>> + if (!cctx->audio_init_mem) {
>>>> + pages[0].addr = cctx->remote_heap->dma_addr;
>>>> + pages[0].size = cctx->remote_heap->size;
>>>> + cctx->audio_init_mem = true;
>>>> + inbuf.pageslen = 1;
>>>> + } else {
>>>> + pages[0].addr = 0;
>>>> + pages[0].size = 0;
>>> What is the expected behavoiur in this case?
>> Audio daemon is expected to take the memory information to DSP audio PD
>> only the first time it goes and attaches there.
>>
>> This is the case where daemon was killed but audio PD session is still
>> running. In such cases, daemon is not expected to take any memory
>> information to audio PD as the earlier shared information is already
>> there with audio PD which it is using irrespective of daemon state.>
> Wow, this behavior is not documented or pl consider adding a comment
> here, Can we not query the dsp before creating new audiopd service?
Agree with your point about documentation, maybe Jianping can add the
information.
audio PD is generally not aware/affected by the state of daemon
process(other than the reverse request break) and there is no such query
supported to see if the audio PD already has the memory information.>
>
> Does it make sense to attach instead of creating?
Calling it attach as PD creation is not done by the daemon, PD is
statically created upon DSP boot-up, the daemon just acts as a listener
for that PD to take up reverse fastrpc request.>
> --srini
>
>>>> + }
>>>> + spin_unlock_irqrestore(&cctx->lock, flags);
>>>>
>>>> args[2].ptr = (u64)(uintptr_t) pages;
>>>> args[2].length = sizeof(*pages);
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-07-01 20:37 ` Srinivas Kandagatla
2026-07-02 4:02 ` Ekansh Gupta
@ 2026-07-03 4:45 ` Jianping Li
1 sibling, 0 replies; 15+ messages in thread
From: Jianping Li @ 2026-07-03 4:45 UTC (permalink / raw)
To: Srinivas Kandagatla
Cc: amahesh, Ekansh Gupta, arnd, Greg KH, abelvesa,
jorge.ramirez-ortiz, linux-arm-msm, dri-devel, linux-kernel,
quic_chennak
On 7/2/2026 4:37 AM, Srinivas Kandagatla wrote:
>
> On 6/9/26 3:59 AM, Jianping Li wrote:
>> Allocating and freeing Audio PD memory from userspace is unsafe because
>> the kernel cannot reliably determine when the DSP has finished using the
>> memory. Userspace may free buffers while they are still in use by the DSP,
>> and remote free requests cannot be safely trusted.
>>
>> Additionally, the current implementation allows userspace to repeatedly
>> grow the Audio PD heap, but does not support shrinking it. This can lead
>> to unbounded memory usage over time, effectively causing a memory leak.
>>
>> Fix this by allocating the entire Audio PD reserved-memory region during
>> rpmsg probe and tying its lifetime to the rpmsg channel. This removes
>> userspace-controlled alloc/free and ensures that memory is reclaimed only
>> when the DSP process is torn down.
>>
>> Add explicit validation for remote_heap presence and size before sending
>> the memory to DSP, and fail early if the reserved-memory region is
>> missing or incomplete.
>>
>> Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd")
>> Cc: stable@kernel.org
>> Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
>> ---
>> drivers/misc/fastrpc.c | 112 ++++++++++++++++++++++-------------------
>> 1 file changed, 59 insertions(+), 53 deletions(-)
>>
>> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
>> index 517884000331..1942e74535e5 100644
>> --- a/drivers/misc/fastrpc.c
>> +++ b/drivers/misc/fastrpc.c
>> @@ -276,6 +276,8 @@ struct fastrpc_channel_ctx {
>> struct kref refcount;
>> /* Flag if dsp attributes are cached */
>> bool valid_attributes;
>> + /* Flag if audio PD init mem was allocated */
>> + bool audio_init_mem;
>> u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES];
>> struct fastrpc_device *secure_fdevice;
>> struct fastrpc_device *fdevice;
>> @@ -1341,15 +1343,24 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>> struct fastrpc_init_create_static init;
>> struct fastrpc_invoke_args *args;
>> struct fastrpc_phy_page pages[1];
>> + struct fastrpc_channel_ctx *cctx = fl->cctx;
>> char *name;
>> int err;
>> - bool scm_done = false;
>> struct {
>> int client_id;
>> u32 namelen;
>> u32 pageslen;
>> } inbuf;
>> u32 sc;
>> + unsigned long flags;
>> +
>> + if (!cctx->remote_heap || !cctx->remote_heap->dma_addr ||
>> + !cctx->remote_heap->size) {
>> + err = -ENOMEM;
>> + dev_err(fl->sctx->dev,
>> + "remote heap memory region is not added\n");
>> + return err;
>> + }
>>
>> args = kzalloc_objs(*args, FASTRPC_CREATE_STATIC_PROCESS_NARGS);
>> if (!args)
>> @@ -1373,31 +1384,6 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>> inbuf.client_id = fl->client_id;
>> inbuf.namelen = init.namelen;
>> inbuf.pageslen = 0;
>> - if (!fl->cctx->remote_heap) {
>> - err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen,
>> - &fl->cctx->remote_heap);
>> - if (err)
>> - goto err_name;
>> -
>> - /* Map if we have any heap VMIDs associated with this ADSP Static Process. */
>> - if (fl->cctx->vmcount) {
>> - u64 src_perms = BIT(QCOM_SCM_VMID_HLOS);
>> -
>> - err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
>> - (u64)fl->cctx->remote_heap->size,
>> - &src_perms,
>> - fl->cctx->vmperms, fl->cctx->vmcount);
>> - if (err) {
>> - dev_err(fl->sctx->dev,
>> - "Failed to assign memory with dma_addr %pad size 0x%llx err %d\n",
>> - &fl->cctx->remote_heap->dma_addr,
>> - fl->cctx->remote_heap->size, err);
>> - goto err_map;
>> - }
>> - scm_done = true;
>> - inbuf.pageslen = 1;
>> - }
>> - }
>>
>> fl->pd = USER_PD;
>>
>> @@ -1409,8 +1395,17 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>> args[1].length = inbuf.namelen;
>> args[1].fd = -1;
>>
>> - pages[0].addr = fl->cctx->remote_heap->dma_addr;
>> - pages[0].size = fl->cctx->remote_heap->size;
>> + spin_lock_irqsave(&cctx->lock, flags);
>> + if (!cctx->audio_init_mem) {
>> + pages[0].addr = cctx->remote_heap->dma_addr;
>> + pages[0].size = cctx->remote_heap->size;
>> + cctx->audio_init_mem = true;
>> + inbuf.pageslen = 1;
>> + } else {
>> + pages[0].addr = 0;
>> + pages[0].size = 0;
> What is the expected behavoiur in this case?
You're right that this behaviour is subtle and deserves an in-code
comment. I'll add one in v9, right above the spin_lock, along the
lines of:
/*
* Audio PD is a static PD and retains the remote heap
* information across daemon restarts. Therefore only
* the first attach should provide heap information to
* DSP. Subsequent attaches reuse the previously
* initialized memory pool.
*/
>
>> + }
>> + spin_unlock_irqrestore(&cctx->lock, flags);
>>
>> args[2].ptr = (u64)(uintptr_t) pages;
>> args[2].length = sizeof(*pages);
>> @@ -1428,27 +1423,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
>>
>> return 0;
>> err_invoke:
>> - if (fl->cctx->vmcount && scm_done) {
>> - u64 src_perms = 0;
>> - struct qcom_scm_vmperm dst_perms;
>> - u32 i;
>> -
>> - for (i = 0; i < fl->cctx->vmcount; i++)
>> - src_perms |= BIT(fl->cctx->vmperms[i].vmid);
>> -
>> - dst_perms.vmid = QCOM_SCM_VMID_HLOS;
>> - dst_perms.perm = QCOM_SCM_PERM_RWX;
>> - err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
>> - (u64)fl->cctx->remote_heap->size,
>> - &src_perms, &dst_perms, 1);
>> - if (err)
>> - dev_err(fl->sctx->dev, "Failed to assign memory dma_addr %pad size 0x%llx err %d\n",
>> - &fl->cctx->remote_heap->dma_addr, fl->cctx->remote_heap->size, err);
>> - }
>> -err_map:
>> - fastrpc_buf_free(fl->cctx->remote_heap);
>> - fl->cctx->remote_heap = NULL;
>> -err_name:
>> + cctx->audio_init_mem = false;
> this can race.
Thanks for the review. I'd like to clarify the intent here — please
let me know if I'm missing something.
The lock is not really there to protect 'audio_init_mem' itself,
but to make sure the following is only sent to the DSP once:
pages[0].addr = fl->cctx->remote_heap->phys;
pages[0].size = fl->cctx->remote_heap->size;
The flow I had in mind is:
- Thread A enters the critical section, sees audio_init_mem == false,
sets it to true, and proceeds to send the pages to the DSP.
- If Thread A's invoke fails, we reset audio_init_mem back to false
in err_invoke so that a later attempt can retry sending the region.
- Once any thread successfully sends the region, audio_init_mem stays
true forever and the false branch is never taken again.
So the reset on failure is only meant to allow a retry, not to be
racy with a concurrent successful path (there can't be one, since
only one thread is inside the critical section at a time). Do you
see a specific interleaving I've overlooked? If so, I'm happy to
switch to a different scheme.
>
>> kfree(name);
>> err:
>> kfree(args);
>> @@ -2415,12 +2390,23 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
>> }
>> }
>>
>> - if (domain_id == SDSP_DOMAIN_ID) {
>> + if (domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID) {
>> struct resource res;
>> u64 src_perms;
>>
>> err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res);
>> if (!err) {
>> + if (domain_id == ADSP_DOMAIN_ID) {
>> + data->remote_heap =
>> + kzalloc_obj(*data->remote_heap, GFP_KERNEL);
>> + if (!data->remote_heap) {
>> + err = -ENOMEM;
>> + goto err_free_data;
>> + }
>> +
>> + data->remote_heap->dma_addr = res.start;
>> + data->remote_heap->size = resource_size(&res);
>> + }
>> src_perms = BIT(QCOM_SCM_VMID_HLOS);
>>
>> err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
>> @@ -2428,7 +2414,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
>> if (err)
>> goto err_free_data;
>> }
>> -
>> }
>>
>> secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
>> @@ -2487,6 +2472,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
>> misc_deregister(&data->secure_fdevice->miscdev);
>>
>> err_free_data:
>> + kfree(data->remote_heap);
>> kfree(data);
>> return err;
>> }
>> @@ -2509,6 +2495,7 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
>> struct fastrpc_buf *buf, *b;
>> struct fastrpc_user *user;
>> unsigned long flags;
>> + int err;
>>
>> /* No invocations past this point */
>> spin_lock_irqsave(&cctx->lock, flags);
>> @@ -2526,8 +2513,27 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
>> list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node)
>> list_del(&buf->node);
>>
>> - if (cctx->remote_heap)
>> - fastrpc_buf_free(cctx->remote_heap);
>> + if (cctx->remote_heap && cctx->vmcount) {
>> + u64 src_perms = 0;
>> + struct qcom_scm_vmperm dst_perms;
>> +
>> + for (u32 i = 0; i < cctx->vmcount; i++)
>> + src_perms |= BIT(cctx->vmperms[i].vmid);
>> +
>> + dst_perms.vmid = QCOM_SCM_VMID_HLOS;
>> + dst_perms.perm = QCOM_SCM_PERM_RWX;
>> +
>> + err = qcom_scm_assign_mem(cctx->remote_heap->dma_addr,
>> + cctx->remote_heap->size, &src_perms,
>> + &dst_perms, 1);
>> + if (err)
>> + dev_err(&rpdev->dev,
>> + "Failed to assign memory back to HLOS: dma_addr %pad size %#llx err %d\n",
>> + &cctx->remote_heap->dma_addr, cctx->remote_heap->size, err);
>> + }
>> +
>> + kfree(cctx->remote_heap);
>> + cctx->remote_heap = NULL;
>>
>> of_platform_depopulate(&rpdev->dev);
>>
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-07-03 4:45 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-09 2:59 [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Jianping Li
2026-06-09 2:59 ` [PATCH v8 1/4] misc: fastrpc: Fix initial memory allocation for Audio PD memory pool Jianping Li
2026-06-09 3:14 ` sashiko-bot
2026-06-09 2:59 ` [PATCH v8 2/4] misc: fastrpc: Remove buffer from list prior to unmap operation Jianping Li
2026-06-09 3:09 ` sashiko-bot
2026-06-09 2:59 ` [PATCH v8 3/4] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Jianping Li
2026-06-09 3:15 ` sashiko-bot
2026-07-01 20:37 ` Srinivas Kandagatla
2026-07-02 4:02 ` Ekansh Gupta
2026-07-02 6:46 ` Srinivas Kandagatla
2026-07-02 8:43 ` Ekansh Gupta
2026-07-03 4:45 ` Jianping Li
2026-06-09 2:59 ` [PATCH v8 4/4] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL Jianping Li
2026-06-09 3:18 ` sashiko-bot
2026-07-01 20:39 ` (subset) [PATCH v8 0/4] misc: fastrpc: Add missing bug fixes Srinivas Kandagatla
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox