* [PATCH v12] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
@ 2026-08-14 10:19 Jianping Li
2026-08-14 10:31 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Jianping Li @ 2026-08-14 10:19 UTC (permalink / raw)
To: Srinivas Kandagatla, Ekansh Gupta
Cc: Jianping Li, Arnd Bergmann, Greg Kroah-Hartman, Abel Vesa,
linux-arm-msm, dri-devel, linux-kernel, 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.
The reserved-memory region is now mandatory for the Audio PD domain.
Rather than failing rpmsg probe when it is missing, validate it in
fastrpc_init_create_static_process() and reject only the static-process
creation. This keeps the fastrpc device probing for all other domains
even on a misconfigured device tree.
Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd")
Cc: stable@kernel.org
Signed-off-by: Jianping Li <jianping.li@oss.qualcomm.com>
---
Patch [v11]: https://lore.kernel.org/all/20260731093210.473-1-jianping.li@oss.qualcomm.com/
Changes in v12:
- Do not fail rpmsg probe when the reserved-memory region is missing,
validate the region in fastrpc_init_create_static_process() instead,
so probe keeps working for all domains.
- Add fastrpc_domain_has_reserved_heap() / fastrpc_domain_uses_static_heap()
helpers to replace the open-coded ADSP/SDSP domain checks.
Changes in v11:
- Replace the remote_heap fastrpc_buf pointer with dedicated
remote_heap_addr and remote_heap_size fields in
fastrpc_channel_ctx to avoid leaving a partially
initialized fastrpc_buf.
- Drop ADSP_MMAP_REMOTE_HEAP_ADDR support from
fastrpc_req_mmap() since the user process should no longer
grow or shrink the Audio PD remote heap.
Changes in v10:
- Move Audio PD remote heap validation into
fastrpc_rpmsg_probe().
- Treat Audio PD remote heap as a mandatory
resource and fail probe if the reserved
memory region is missing.
Changes in v9:
- Make sure fastrpc_init_create_static_process()
only sets audio_init_mem to false when the sent
address is actually invalid.
---
drivers/misc/fastrpc.c | 150 +++++++++++++++++++++--------------------
1 file changed, 76 insertions(+), 74 deletions(-)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 90fd669636ec..3f14a4673698 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -70,8 +70,6 @@
#define ADSP_MMAP_HEAP_ADDR 4
/* MAP static DMA buffer on DSP User PD */
#define ADSP_MMAP_DMA_BUFFER 6
-/* Add memory to static PD pool protection thru hypervisor */
-#define ADSP_MMAP_REMOTE_HEAP_ADDR 8
/* Add memory to userPD pool, for user heap */
#define ADSP_MMAP_ADD_PAGES 0x1000
/* Add memory to userPD pool, for LLC heap */
@@ -314,10 +312,14 @@ 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;
+ /* Audio PD reserved remote heap region */
+ phys_addr_t remote_heap_addr;
+ u64 remote_heap_size;
u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES];
struct fastrpc_device *secure_fdevice;
struct fastrpc_device *fdevice;
- struct fastrpc_buf *remote_heap;
struct list_head invoke_interrupted_mmaps;
bool secure;
bool unsigned_support;
@@ -1454,15 +1456,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;
+ bool sent_heap = false;
+
+ if (!cctx->remote_heap_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)
@@ -1486,31 +1497,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;
@@ -1522,8 +1508,25 @@ 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;
+ /*
+ * 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_lock_irqsave(&cctx->lock, flags);
+ if (!cctx->audio_init_mem) {
+ pages[0].addr = cctx->remote_heap_addr;
+ pages[0].size = cctx->remote_heap_size;
+ cctx->audio_init_mem = true;
+ inbuf.pageslen = 1;
+ sent_heap = true;
+ } 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);
@@ -1541,27 +1544,11 @@ 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);
+ if (sent_heap) {
+ spin_lock_irqsave(&cctx->lock, flags);
+ cctx->audio_init_mem = false;
+ spin_unlock_irqrestore(&cctx->lock, flags);
}
-err_map:
- fastrpc_buf_free(fl->cctx->remote_heap);
- fl->cctx->remote_heap = NULL;
-err_name:
kfree(name);
err:
kfree(args);
@@ -2090,7 +2077,7 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
- if (req.flags != ADSP_MMAP_ADD_PAGES && req.flags != ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ if (req.flags != ADSP_MMAP_ADD_PAGES) {
dev_err(dev, "flag not supported 0x%x\n", req.flags);
return -EINVAL;
@@ -2101,10 +2088,7 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
return -EINVAL;
}
- if (req.flags == ADSP_MMAP_REMOTE_HEAP_ADDR)
- err = fastrpc_remote_heap_alloc(fl, dev, req.size, &buf);
- else
- err = fastrpc_buf_alloc(fl, dev, req.size, &buf);
+ err = fastrpc_buf_alloc(fl, dev, req.size, &buf);
if (err) {
dev_err(dev, "failed to allocate buffer\n");
@@ -2143,20 +2127,6 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
/* let the client know the address to use */
req.vaddrout = rsp_msg.vaddr;
- /* Add memory to static PD pool, protection thru hypervisor */
- if (req.flags == ADSP_MMAP_REMOTE_HEAP_ADDR && fl->cctx->vmcount) {
- u64 src_perms = BIT(QCOM_SCM_VMID_HLOS);
-
- err = qcom_scm_assign_mem(buf->dma_addr, (u64)buf->size,
- &src_perms, fl->cctx->vmperms, fl->cctx->vmcount);
- if (err) {
- dev_err(fl->sctx->dev,
- "Failed to assign memory dma_addr %pad size 0x%llx err %d",
- &buf->dma_addr, buf->size, err);
- goto err_assign;
- }
- }
-
spin_lock(&fl->lock);
list_add_tail(&buf->node, &fl->mmaps);
spin_unlock(&fl->lock);
@@ -2537,6 +2507,16 @@ static const struct of_device_id fastrpc_poll_supported_machines[] __maybe_unuse
{},
};
+static bool fastrpc_domain_has_reserved_heap(u32 domain_id)
+{
+ return domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID;
+}
+
+static bool fastrpc_domain_uses_static_heap(u32 domain_id)
+{
+ return domain_id == ADSP_DOMAIN_ID;
+}
+
static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct device *rdev = &rpdev->dev;
@@ -2584,20 +2564,25 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
}
}
- if (domain_id == SDSP_DOMAIN_ID) {
+ if (fastrpc_domain_has_reserved_heap(domain_id)) {
struct resource res;
u64 src_perms;
err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res);
if (!err) {
+ if (fastrpc_domain_uses_static_heap(domain_id)) {
+ data->remote_heap_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,
data->vmperms, data->vmcount);
if (err)
goto err_free_data;
+ } else {
+ err = 0;
}
-
}
secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
@@ -2681,6 +2666,7 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
struct fastrpc_buf *buf, *b;
struct fastrpc_user *user;
unsigned long flags;
+ int err, i;
/* No invocations past this point */
spin_lock_irqsave(&cctx->lock, flags);
@@ -2698,8 +2684,24 @@ 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_size && cctx->vmcount) {
+ u64 src_perms = 0;
+ struct qcom_scm_vmperm dst_perms;
+
+ for (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_addr,
+ cctx->remote_heap_size, &src_perms,
+ &dst_perms, 1);
+ if (err)
+ dev_err(&rpdev->dev,
+ "Failed to assign memory back to HLOS: addr %pa size %#llx err %d\n",
+ &cctx->remote_heap_addr, cctx->remote_heap_size, err);
+ }
of_platform_depopulate(&rpdev->dev);
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH v12] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
2026-08-14 10:19 [PATCH v12] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Jianping Li
@ 2026-08-14 10:31 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-08-14 10:31 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] TrustZone memory assignment is permanently leaked on probe error paths.
- [High] Concurrent ioctl calls can cause the DSP to receive an uninitialized static process creation request out-of-order.
- [Medium] Unconditional TrustZone memory assignment during probe may fail or orphan memory for ADSP devices lacking VMIDs.
- [Medium] The `audio_init_mem` flag is incorrectly rolled back on remote invocation errors, allowing the static heap to be sent multiple times.
Pre-existing issues:
- [High] Permanent TrustZone memory leak for SDSP domains during driver removal.
--
--- Patch [1]: [PATCH v12] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe ---
commit f78e580b5ae96a440902b976778bd1f19b384768
Author: Jianping Li <jianping.li@oss.qualcomm.com>
misc: fastrpc: Allocate entire reserved memory for Audio PD in probe
This commit allocates the entire Audio PD reserved-memory region during
rpmsg probe and ties its lifetime to the rpmsg channel. It removes
userspace-controlled allocation and freeing, ensuring memory is only
reclaimed when the DSP process is torn down.
Link: https://lore.kernel.org/all/20260731093210.473-1-jianping.li@oss.qualcomm.com/
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index 90fd669636ec1..3f14a4673698e 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
[ ... ]
> @@ -1508,17 +1508,34 @@ 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;
> + /*
> + * 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_lock_irqsave(&cctx->lock, flags);
> + if (!cctx->audio_init_mem) {
> + pages[0].addr = cctx->remote_heap_addr;
> + pages[0].size = cctx->remote_heap_size;
> + cctx->audio_init_mem = true;
> + inbuf.pageslen = 1;
> + sent_heap = true;
> + } 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);
> args[2].fd = -1;
>
> sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_CREATE_STATIC, 3, 0);
>
> err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE,
> sc, args);
[Severity: High]
Could this allow the DSP to receive an uninitialized static process creation
request out-of-order?
Two threads could concurrently execute the ioctl. Thread A acquires the lock,
sees audio_init_mem is false, sets it to true, and sets pageslen = 1.
Thread B then acquires the lock, sees audio_init_mem is true, and sets
pageslen = 0.
Because fastrpc_internal_invoke() is called outside the lock, thread execution
order is unconstrained once the lock is released. Thread B could be scheduled
such that its RPC payload (with pageslen = 0) reaches the DSP before Thread
A's initialization payload.
> if (err)
> goto err_invoke;
>
> kfree(args);
> kfree(name);
>
> return 0;
> err_invoke:
> - if (fl->cctx->vmcount && scm_done) {
[ ... ]
> + if (sent_heap) {
> + spin_lock_irqsave(&cctx->lock, flags);
> + cctx->audio_init_mem = false;
> + spin_unlock_irqrestore(&cctx->lock, flags);
> }
> -err_map:
> - fastrpc_buf_free(fl->cctx->remote_heap);
> - fl->cctx->remote_heap = NULL;
> -err_name:
> kfree(name);
> err:
[Severity: Medium]
Is it safe to blindly roll back audio_init_mem if fastrpc_internal_invoke()
returns an error?
Because the DSP operates asynchronously, an interrupted kernel wait (such as
-ERESTARTSYS due to a received signal) does not necessarily mean the DSP
failed to process the request. Reverting this flag might allow the next
process attachment to send the heap configuration pages a second time, which
would violate the static process lifecycle.
[ ... ]
> @@ -2564,25 +2564,20 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
[ ... ]
> - if (domain_id == SDSP_DOMAIN_ID) {
> + if (fastrpc_domain_has_reserved_heap(domain_id)) {
> struct resource res;
> u64 src_perms;
>
> err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res);
> if (!err) {
> + if (fastrpc_domain_uses_static_heap(domain_id)) {
> + data->remote_heap_addr = res.start;
> + data->remote_heap_size = resource_size(&res);
> + }
[Severity: High]
This is a pre-existing issue, but does this logic permanently leak TrustZone
memory for SDSP domains when the driver is removed?
For SDSP domains, fastrpc_domain_has_reserved_heap() returns true, so memory
is assigned to the DSP VMID below. However,
fastrpc_domain_uses_static_heap() returns false for SDSP, so
data->remote_heap_size remains uninitialized as 0.
> src_perms = BIT(QCOM_SCM_VMID_HLOS);
>
> err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
> data->vmperms, data->vmcount);
[Severity: Medium]
Is it intentional to call qcom_scm_assign_mem() unconditionally here, even
if data->vmcount is zero?
Previously, this assignment was explicitly guarded by checking vmcount.
If a device tree without the qcom,vmids property initializes the ADSP domain,
could calling this with a destination count of 0 be rejected by TrustZone
and fail the probe? Additionally, if vmcount is 0, the memory is explicitly
skipped during cleanup, which would permanently leak it.
> if (err)
> goto err_free_data;
> + } else {
> + err = 0;
> }
> -
> }
[ ... ]
> err_free_data:
> kfree(data);
> return err;
> }
[Severity: High]
Does this error path permanently leak the TrustZone memory assignment?
If any operation fails after the memory is successfully assigned to the DSP
VMID (for example, inside fastrpc_device_register() or
of_platform_populate()), the error handlers only free local data structures.
They do not appear to call qcom_scm_assign_mem() to revert the TrustZone
memory ownership back to HLOS.
[ ... ]
> @@ -2684,24 +2684,8 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
[ ... ]
> - if (cctx->remote_heap)
> - fastrpc_buf_free(cctx->remote_heap);
> + if (cctx->remote_heap_size && cctx->vmcount) {
> + u64 src_perms = 0;
> + struct qcom_scm_vmperm dst_perms;
[Severity: High]
This is a pre-existing issue, but does this condition permanently bypass
memory unassignment for SDSP domains?
Because remote_heap_size is intentionally left as 0 for SDSP domains during
probe, this cleanup block is skipped. This leaves the reserved memory
permanently assigned to the DSP VMID after module removal.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814101955.234238-1-jianping.li@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-14 10:31 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14 10:19 [PATCH v12] misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Jianping Li
2026-08-14 10:31 ` 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.