From: sashiko-bot@kernel.org
To: "Ekansh Gupta" <ekansh.gupta@oss.qualcomm.com>
Cc: dri-devel@lists.freedesktop.org, media-ci@linuxtv.org
Subject: Re: [PATCH v4 19/25] accel/qda: Add remote memory map and unmap to the legacy shim
Date: Sun, 01 Nov 2026 22:57:29 +0000 [thread overview]
Message-ID: <sashiko-outbox-192744@kernel.org> (raw)
In-Reply-To: <20261102-v4-v4-19-88f2f24a5f0b@qti.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Critical] accel/qda: use-after-free race on placeholder in qda_shim_mem_map()
- [High] accel/qda: unmap size mismatch in qda_shim_mmap() when size is 0
- [High] accel/qda: missing unplug check in shim_mappings_release() on close
- [High] accel/qda: premature buffer release when legacy unmap fails or is interrupted
- [Medium] accel/qda: race in shim_mapping_detach() and shim_mapping_attach()
- [Medium] accel/qda: missing offset bounds check in setup_mmap_pages()
--
commit b1739b465f1ac98dac259c9a90724960699924f1
Author: Ekansh Gupta <ekangupt@qti.qualcomm.com>
accel/qda: Add remote memory map and unmap to the legacy shim
This commit implements the four legacy FastRPC memory map and unmap ioctls
in the QDA compatibility shim by importing DMA-BUF file descriptors and
tracking live mappings per open file so buffers remain referenced while
mapped on the DSP.
> diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
> index dbd14cd07cb20..b35604fce630c 100644
> --- a/drivers/accel/qda/qda_fastrpc.c
> +++ b/drivers/accel/qda/qda_fastrpc.c
[ ... ]
> @@ -935,19 +935,33 @@ static int qda_fastrpc_prepare_args_init_create(struct qda_fastrpc_invoke_ctx *c
> * buffer is held by the context until the request is done, so that closing its
> * handle meanwhile cannot free memory the DSP is being told to map. The
> * region must fit in the buffer, as the DSP would otherwise map past its end.
> + *
> + * A caller with no handle table supplies the buffer already imported. It is
> + * checked as a buffer named by handle would be: an fd can name a buffer that
> + * another session exported, which resolves to the original object, still
> + * mapped in that session's context bank.
> */
> -static int setup_mmap_pages(struct qda_fastrpc_invoke_ctx *ctx, struct drm_qda_mem_map *margs,
> +static int setup_mmap_pages(struct qda_fastrpc_invoke_ctx *ctx, struct qda_mem_map_args *args,
> struct qda_phy_page *pages)
> {
> + struct drm_qda_mem_map *margs = &args->map;
> struct qda_gem_obj *qda_gem_obj;
> int err;
>
> - if (!margs->handle)
> - return -EINVAL;
> + if (args->obj) {
> + if (to_qda_gem_obj(args->obj)->iommu_dev != ctx->session->assigned_iommu_dev)
> + return -EACCES;
>
> - err = get_gem_obj_from_handle(ctx, margs->handle, &ctx->map_obj);
> - if (err)
> - return err;
> + drm_gem_object_get(args->obj);
> + ctx->map_obj = args->obj;
> + } else {
> + if (!margs->handle)
> + return -EINVAL;
> +
> + err = get_gem_obj_from_handle(ctx, margs->handle, &ctx->map_obj);
> + if (err)
> + return err;
> + }
>
> qda_gem_obj = to_qda_gem_obj(ctx->map_obj);
> if (margs->size > qda_gem_obj->size)
> return -EINVAL;
>
> pages->addr = qda_gem_obj->dma_addr;
> pages->size = margs->size ? margs->size : qda_gem_obj->size;
[Severity: Medium]
Can a caller of FASTRPC_IOCTL_MEM_MAP or DRM_IOCTL_QDA_REMOTE_MMAP (with
QDA_REQUEST_MEM_MAP) map past the end of qda_gem_obj by supplying a
non-zero margs->offset?
In qda_shim_mem_map(), only args.offset < 0 is rejected, and
qda_fastrpc_prepare_args_mem_map() only checks margs->offset > INT_MAX
before setting req->offset = margs->offset.
Here in setup_mmap_pages(), only margs->size > qda_gem_obj->size is
checked. If a caller passes 0 < margs->offset <= INT_MAX and
margs->size <= qda_gem_obj->size (including margs->size == 0, which sets
pages->size to qda_gem_obj->size) such that margs->offset + pages->size
exceeds qda_gem_obj->size, shouldn't setup_mmap_pages() also check that
margs->offset <= qda_gem_obj->size and pages->size <= qda_gem_obj->size -
margs->offset?
> diff --git a/drivers/accel/qda/qda_fastrpc_shim.c b/drivers/accel/qda/qda_fastrpc_shim.c
> index 4a2ba20ee01ac..c46d2de4f5818 100644
> --- a/drivers/accel/qda/qda_fastrpc_shim.c
> +++ b/drivers/accel/qda/qda_fastrpc_shim.c
[ ... ]
> @@ -79,6 +101,83 @@ static struct drm_gem_object *shim_import_fd(struct qda_session *session, s32 fd
> return obj;
> }
>
> +/* Takes ownership of @obj on success; on failure the caller still owns it */
> +static struct qda_shim_mapping *shim_mapping_add(struct qda_shim_file *sf,
> + struct list_head *list, u64 key, u64 vaddr,
> + u64 size, struct drm_gem_object *obj)
> +{
> + struct qda_shim_mapping *m, *new;
> +
> + new = kzalloc_obj(*new);
> + if (!new)
> + return ERR_PTR(-ENOMEM);
> +
> + new->key = key;
> + new->vaddr = vaddr;
> + new->size = size;
> + new->obj = obj;
> +
> + guard(mutex)(&sf->maps_lock);
> +
> + list_for_each_entry(m, list, node) {
> + if (m->key == key) {
> + kfree(new);
> + return ERR_PTR(-EEXIST);
> + }
> + }
> + list_add_tail(&new->node, list);
> +
> + return new;
> +}
> +
> +static struct qda_shim_mapping *shim_mapping_detach(struct qda_shim_file *sf,
> + struct list_head *list, u64 key, u64 vaddr)
> +{
> + struct qda_shim_mapping *m;
> +
> + guard(mutex)(&sf->maps_lock);
> +
> + list_for_each_entry(m, list, node) {
> + if (m->key == key && m->vaddr == vaddr) {
> + list_del(&m->node);
> + return m;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static void shim_mapping_attach(struct qda_shim_file *sf, struct list_head *list,
> + struct qda_shim_mapping *m)
> +{
> + guard(mutex)(&sf->maps_lock);
> + list_add_tail(&m->node, list);
> +}
[Severity: Medium]
Can detaching a mapping in shim_mapping_detach() and dropping sf->maps_lock
before validating size or calling the blocking unmap RPC race with
concurrent map and unmap calls on the same open file?
First, in qda_shim_mem_unmap(), m is removed from sf->mem_map_list while
qda_legacy_ops_table.mem_unmap() runs without sf->maps_lock held. While m
is detached, its key (args.fd) is absent from sf->mem_map_list, so a
concurrent qda_shim_mem_map() with the same fd can pass the duplicate-key
check in shim_mapping_add() and insert a second entry with the same fd. If
the in-flight mem_unmap() then fails (for example, interrupted by a signal
with -ERESTARTSYS in wait_for_completion_interruptible()),
shim_mapping_attach() unconditionally appends m back onto sf->mem_map_list
with list_add_tail(), leaving duplicate fd keys in sf->mem_map_list.
Second, in both qda_shim_mmap() and qda_shim_mem_map(), m is visible on the
list before copy_to_user() runs. If a concurrent qda_shim_munmap() (for
example, with a mismatched args.size since m->size != args.size is checked
after shim_mapping_detach()) or a failing qda_shim_mem_unmap() temporarily
detaches m while copy_to_user() faults, the error rollback call to
shim_mapping_detach() in the map path will return NULL and skip unmapping
and freeing m, after which the failing unmap path calls
shim_mapping_attach() and re-attaches m even though the map ioctl returned
-EFAULT.
> +
> +static void shim_mapping_free(struct qda_shim_mapping *m)
> +{
> + drm_gem_object_put(m->obj);
> + kfree(m);
> +}
> +
> +/* The DSP unmap comes first: the buffer must outlive the DSP's use of it */
> +static void shim_mappings_release(struct qda_shim_file *sf)
> +{
> + struct qda_shim_mapping *m, *tmp;
> +
> + list_for_each_entry_safe(m, tmp, &sf->mmap_list, node) {
> + list_del(&m->node);
> + qda_legacy_ops_table.munmap(sf->session, m->vaddr, m->size);
> + shim_mapping_free(m);
> + }
> +
> + list_for_each_entry_safe(m, tmp, &sf->mem_map_list, node) {
> + list_del(&m->node);
> + qda_legacy_ops_table.mem_unmap(sf->session, m->key, m->vaddr, m->size);
> + shim_mapping_free(m);
> + }
> +}
[Severity: High]
What happens in qda_shim_release() -> shim_mappings_release() if the file
is closed after the underlying rpmsg device has been removed?
When the rpmsg device is removed while a /dev/fastrpc-* file is still open,
qda_rpmsg_remove() calls drm_dev_unplug(), qda_cb_unpopulate() (destroying
each context-bank platform device), and qda_deinit_device() (destroying
qdev->iommu_mgr->assignment_lock and qdev->ctx_xa), and devres frees
qdev->iommu_mgr.
In drivers/accel/qda/qda_legacy_ops.c, legacy_release() checks
drm_dev_is_unplugged(&session->qda_dev->drm_dev) before calling
qda_release_dsp_process(). However, qda_shim_release() calls
shim_mappings_release() before legacy_release(), and neither
shim_mappings_release() nor legacy_munmap() and legacy_mem_unmap() checks
drm_dev_is_unplugged(). Won't closing a file with active mappings after
unplug call into qda_fastrpc_invoke() -> xa_alloc(&qdev->ctx_xa, ...) and
alloc_kernel_gem() -> qda_memory_manager_alloc(), dereferencing the
destroyed ctx_xa, devm-freed qdev->iommu_mgr, and destroyed context-bank
platform device?
[ ... ]
> @@ -191,6 +295,156 @@ static long qda_shim_init_create(struct qda_shim_file *sf, void __user *argp)
> return err;
> }
>
> +static long qda_shim_mmap(struct qda_shim_file *sf, void __user *argp)
> +{
> + struct qda_shim_mapping *m;
> + struct fastrpc_req_mmap args;
> + struct drm_gem_object *obj;
> + int err;
> +
> + if (copy_from_user(&args, argp, sizeof(args)))
> + return -EFAULT;
> +
> + obj = shim_import_fd(sf->session, args.fd);
> + if (IS_ERR(obj))
> + return PTR_ERR(obj);
> + if (!obj)
> + return -EINVAL;
> +
> + err = qda_legacy_ops_table.mmap(sf->session, obj, args.vaddrin, args.size, args.flags,
> + &args.vaddrout);
> + if (err) {
> + drm_gem_object_put(obj);
> + return err;
> + }
> +
> + m = shim_mapping_add(sf, &sf->mmap_list, args.vaddrout, args.vaddrout, args.size, obj);
[Severity: High]
If user space passes args.size == 0 to FASTRPC_IOCTL_MMAP (or
args.length == 0 to FASTRPC_IOCTL_MEM_MAP in qda_shim_mem_map()), doesn't
this record m->size = 0 while the DSP actually mapped the full GEM object
size?
In setup_mmap_pages():
pages->addr = qda_gem_obj->dma_addr;
pages->size = margs->size ? margs->size : qda_gem_obj->size;
When args.size is 0, the DSP maps qda_gem_obj->size bytes, but
qda_shim_mmap() and qda_shim_mem_map() record 0 in m->size. When the
mapping is later unmapped via qda_shim_munmap(), qda_shim_mem_unmap(),
copy_to_user() failure rollback, or shim_mappings_release(), the DSP is
asked to unmap 0 bytes before shim_mapping_free() drops the reference on
obj, and FASTRPC_IOCTL_MUNMAP calls specifying the actual buffer size will
be rejected by m->size != args.size. Should m->size record the effective
mapped size (or should a zero size/length be rejected)?
> + if (IS_ERR(m)) {
> + err = PTR_ERR(m);
> + goto err_unmap;
> + }
> +
> + if (copy_to_user(argp, &args, sizeof(args))) {
> + m = shim_mapping_detach(sf, &sf->mmap_list, args.vaddrout, args.vaddrout);
> + if (m) {
> + qda_legacy_ops_table.munmap(sf->session, m->vaddr, m->size);
> + shim_mapping_free(m);
> + }
> + return -EFAULT;
> + }
> +
> + return 0;
> +
> +err_unmap:
> + qda_legacy_ops_table.munmap(sf->session, args.vaddrout, args.size);
> + drm_gem_object_put(obj);
> +
> + return err;
> +}
[Severity: High]
Can ignoring the return value of munmap() and mem_unmap() in these rollback
paths (and in shim_mappings_release()) release m->obj while the DSP still
has the buffer mapped?
Here in qda_shim_mmap() (on copy_to_user() failure and at err_unmap), in
qda_shim_mem_map() (on copy_to_user() failure), and in
shim_mappings_release() on file close, qda_legacy_ops_table.munmap() or
mem_unmap() waits in qda_rpmsg_wait_for_rsp() via
wait_for_completion_interruptible(&ctx->work).
If a signal is pending or arrives while waiting (returning -ERESTARTSYS),
or if allocating the unmap invocation context fails, the DSP has not
unmapped the buffer, yet shim_mapping_free(m) or drm_gem_object_put(obj)
immediately drops the reference on the GEM object (which for an imported
DMA-BUF in the shim is the sole reference, tearing down its IOMMU mapping
or freeing its DMA memory in qda_gem_free_object()).
[ ... ]
> +static long qda_shim_mem_map(struct qda_shim_file *sf, void __user *argp)
> +{
> + struct qda_shim_mapping *m;
> + struct fastrpc_mem_map args;
> + struct drm_gem_object *obj;
> + int err;
> +
> + if (copy_from_user(&args, argp, sizeof(args)))
> + return -EFAULT;
> +
> + if (args.offset < 0)
> + return -EINVAL;
> +
> + obj = shim_import_fd(sf->session, args.fd);
> + if (IS_ERR(obj))
> + return PTR_ERR(obj);
> + if (!obj)
> + return -EINVAL;
> +
> + /* Reserved before the DSP is asked, so the same fd cannot be mapped twice */
> + m = shim_mapping_add(sf, &sf->mem_map_list, args.fd, 0, args.length, obj);
> + if (IS_ERR(m)) {
> + drm_gem_object_put(obj);
> + return PTR_ERR(m);
> + }
> +
> + err = qda_legacy_ops_table.mem_map(sf->session, obj, args.flags, args.attrs, args.offset,
> + args.vaddrin, args.length, args.fd, &args.vaddrout);
> + if (err) {
> + m = shim_mapping_detach(sf, &sf->mem_map_list, args.fd, 0);
> + if (m)
> + shim_mapping_free(m);
> + return err;
> + }
> +
> + scoped_guard(mutex, &sf->maps_lock)
> + m->vaddr = args.vaddrout;
[Severity: Critical]
Can a concurrent FASTRPC_IOCTL_MEM_UNMAP with args.vaddr == 0 detach and
free the placeholder m while qda_legacy_ops_table.mem_map() is still
running?
In qda_shim_mem_map(), shim_mapping_add() inserts m into sf->mem_map_list
with m->key = args.fd and m->vaddr = 0, and transfers the caller's only
reference on obj into m->obj before dropping sf->maps_lock:
m = shim_mapping_add(sf, &sf->mem_map_list, args.fd, 0, args.length, obj);
...
err = qda_legacy_ops_table.mem_map(sf->session, obj, ...);
While qda_legacy_ops_table.mem_map() is executing without sf->maps_lock, a
concurrent qda_shim_mem_unmap() on the same file with the same args.fd and
args.vaddr == 0 will match the placeholder in shim_mapping_detach():
list_for_each_entry(m, list, node) {
if (m->key == key && m->vaddr == vaddr) {
list_del(&m->node);
return m;
}
}
If qda_legacy_ops_table.mem_unmap() then succeeds, qda_shim_mem_unmap()
calls shim_mapping_free(m), which puts m->obj (potentially before
setup_mmap_pages() has called drm_gem_object_get(args->obj)) and frees m.
When qda_legacy_ops_table.mem_map() finishes and returns 0,
qda_shim_mem_map() writes m->vaddr = args.vaddrout to the freed m and
leaves the DSP mapping active with m->obj already released.
Conversely, if qda_legacy_ops_table.mem_map() fails while the concurrent
qda_shim_mem_unmap() has detached m, qda_shim_mem_map()'s cleanup
shim_mapping_detach() returns NULL, and if mem_unmap() then fails and calls
shim_mapping_attach(), the failed placeholder with vaddr == 0 is
re-attached to sf->mem_map_list, leaking m->obj until file close and
blocking future FASTRPC_IOCTL_MEM_MAP calls on that fd with -EEXIST.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102-v4-v4-0-88f2f24a5f0b@qti.qualcomm.com?part=19
next prev parent reply other threads:[~2026-11-01 22:57 UTC|newest]
Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-01 22:21 [PATCH v4 00/25] accel/qda: Qualcomm DSP Accelerator driver Ekansh Gupta
2026-11-01 22:21 ` [PATCH v4 01/25] accel/qda: Add QDA driver documentation Ekansh Gupta
2026-11-01 22:21 ` [PATCH v4 02/25] accel/qda: Add initial QDA DRM accelerator driver Ekansh Gupta
2026-11-01 22:39 ` sashiko-bot
2026-11-02 8:42 ` Krzysztof Kozlowski
2026-11-01 22:21 ` [PATCH v4 03/25] accel/qda: Add compute context bank devices Ekansh Gupta
2026-11-01 22:52 ` sashiko-bot
2026-11-02 8:43 ` Krzysztof Kozlowski
2026-11-01 22:21 ` [PATCH v4 04/25] accel/qda: Assign a context bank to each session Ekansh Gupta
2026-11-01 22:39 ` sashiko-bot
2026-11-02 8:45 ` Krzysztof Kozlowski
2026-11-01 22:21 ` [PATCH v4 05/25] accel/qda: Add UAPI header and DSP query ioctl Ekansh Gupta
2026-11-01 22:21 ` [PATCH v4 06/25] accel/qda: Add buffer allocation for the DSP Ekansh Gupta
2026-11-01 22:51 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 07/25] accel/qda: Add DMA-BUF import Ekansh Gupta
2026-11-01 22:51 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 08/25] accel/qda: Add legacy ops base and auxiliary device Ekansh Gupta
2026-11-01 22:43 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 09/25] accel/qda: Add legacy FastRPC shim skeleton Ekansh Gupta
2026-11-01 22:42 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 10/25] accel/qda: Add FastRPC invocation contexts Ekansh Gupta
2026-11-01 22:44 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 11/25] accel/qda: Add RPMsg send and response dispatch Ekansh Gupta
2026-11-01 22:39 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 12/25] accel/qda: Add invocation argument validation and packing Ekansh Gupta
2026-11-01 22:44 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 13/25] accel/qda: Add the invocation ioctl and teardown on unplug Ekansh Gupta
2026-11-01 22:41 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 14/25] accel/qda: Add invocation through the legacy FastRPC shim Ekansh Gupta
2026-11-01 22:47 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 15/25] accel/qda: Add DSP process creation and release Ekansh Gupta
2026-11-01 22:50 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 16/25] accel/qda: Add DSP process creation and release to the legacy shim Ekansh Gupta
2026-11-01 22:48 ` sashiko-bot
2026-11-02 8:47 ` Krzysztof Kozlowski
2026-11-01 22:21 ` [PATCH v4 17/25] accel/qda: Add remote memory mapping to the DSP address space Ekansh Gupta
2026-11-01 22:54 ` sashiko-bot
2026-11-02 9:24 ` Konrad Dybcio
2026-11-01 22:21 ` [PATCH v4 18/25] accel/qda: Add remote memory unmapping from " Ekansh Gupta
2026-11-01 22:49 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 19/25] accel/qda: Add remote memory map and unmap to the legacy shim Ekansh Gupta
2026-11-01 22:57 ` sashiko-bot [this message]
2026-11-01 22:21 ` [PATCH v4 20/25] accel/qda: Add remote attach to root and sensors protection domains Ekansh Gupta
2026-11-01 22:49 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 21/25] accel/qda: Add Audio protection domain attach and its remote heap Ekansh Gupta
2026-11-01 23:00 ` sashiko-bot
2026-11-02 8:43 ` Arnd Bergmann
2026-11-02 8:50 ` Krzysztof Kozlowski
2026-11-01 22:21 ` [PATCH v4 22/25] accel/qda: Add remote session attach to the legacy shim Ekansh Gupta
2026-11-01 23:00 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 23/25] accel/qda: Add DSP capability query Ekansh Gupta
2026-11-01 23:03 ` sashiko-bot
2026-11-02 8:46 ` Arnd Bergmann
2026-11-01 22:21 ` [PATCH v4 24/25] accel/qda: Add legacy shim support for " Ekansh Gupta
2026-11-01 23:05 ` sashiko-bot
2026-11-01 22:21 ` [PATCH v4 25/25] accel/qda: Add poll mode for invocation completion Ekansh Gupta
2026-11-01 23:02 ` sashiko-bot
2026-11-02 8:53 ` [PATCH v4 00/25] accel/qda: Qualcomm DSP Accelerator driver Krzysztof Kozlowski
2026-11-02 10:08 ` Ekansh Gupta
2026-11-02 10:12 ` Krzysztof Kozlowski
2026-11-02 10:52 ` Ekansh Gupta
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-192744@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=ekansh.gupta@oss.qualcomm.com \
--cc=media-ci@linuxtv.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.