All of lore.kernel.org
 help / color / mirror / Atom feed
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 07/25] accel/qda: Add DMA-BUF import
Date: Sun, 01 Nov 2026 22:51:46 +0000	[thread overview]
Message-ID: <sashiko-outbox-192740@kernel.org> (raw)
In-Reply-To: <20261102-v4-v4-7-88f2f24a5f0b@qti.qualcomm.com>

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] accel/qda: unreferenced iommu_dev in qda_gem_obj causes UAF and CB reuse
- [High] accel/qda: UAF on CB device unplug in qda_gem_free_object() and import
- [High] accel/qda: missing vm_ops in qda_gem_object_funcs leaks GEM ref on mmap
- [Medium] accel/qda: 32-bit dma_addr_t truncates SID in qda_gem_prime_import_sg_table()
- [Low] accel/qda: unused gem_prime_import_sg_table hook and stale import comments
- [Low] accel/qda: qda_gem_prime_import_sg_table() allocates unused shmem file
--

commit c21f00e936e2fb1a3bd269969e95dd625e477ff7
Author: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>

accel/qda: Add DMA-BUF import

This commit adds DMA-BUF PRIME import and sg_table export support to the
QDA accelerator driver, mapping imported buffers into the importing
session's IOMMU context bank and verifying that mappings are physically
or IOVA contiguous.

> diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
> index 37019ff38ecbc..5e953d1f98bf4 100644
> --- a/drivers/accel/qda/qda_drv.c
> +++ b/drivers/accel/qda/qda_drv.c
[ ... ]
> @@ -69,6 +70,9 @@ static const struct drm_driver qda_drm_driver = {
>  	.postclose = qda_postclose,
>  	.ioctls = qda_ioctls,
>  	.num_ioctls = ARRAY_SIZE(qda_ioctls),
> +	.gem_prime_import = qda_gem_prime_import,
> +	.gem_prime_import_sg_table = qda_gem_prime_import_sg_table,
> +	.prime_fd_to_handle = qda_prime_fd_to_handle,
>  	.name = QDA_DRIVER_NAME,
>  	.desc = "Qualcomm DSP Accelerator Driver",
>  };

[Severity: Low]
This isn't a bug, but is .gem_prime_import_sg_table in qda_drm_driver ever
called by the DRM core?

In DRM PRIME, dev->driver->gem_prime_import_sg_table is only invoked by
drm_gem_prime_import_dev(). However, qda_gem_prime_import() does not call
drm_gem_prime_import_dev(); it open-codes dma_buf_attach() and
dma_buf_map_attachment_unlocked() and calls qda_gem_prime_import_sg_table()
directly.

The commit message ("The core PRIME helper takes the device to attach to, so
pass the session's context bank") and the kernel-doc comment on
qda_gem_prime_import_sg_table() ("Called by the DRM PRIME helpers once they
have attached to the exporter...") also describe the
drm_gem_prime_import_dev() flow rather than the open-coded path in
qda_gem_prime_import(). Could qda_gem_prime_import() either call
drm_gem_prime_import_dev(drm_dev, dma_buf, session->assigned_iommu_dev->dev)
after handling the same-device check, or drop the unused hook and update the
comments?

> diff --git a/drivers/accel/qda/qda_gem.c b/drivers/accel/qda/qda_gem.c
> index 864bac914e48d..a9ab8ccb78930 100644
> --- a/drivers/accel/qda/qda_gem.c
> +++ b/drivers/accel/qda/qda_gem.c
[ ... ]
> @@ -24,6 +31,10 @@ static int qda_gem_mmap_obj(struct drm_gem_object *gem_obj, struct vm_area_struc
>  	struct qda_gem_obj *qda_gem_obj = to_qda_gem_obj(gem_obj);
>  	int ret;
>  
> +	/* Imported buffers have to be mapped through the exporter */
> +	if (drm_gem_is_imported(gem_obj))
> +		return -EINVAL;
> +
>  	/* The fake offset only identifies the object, it does not index into it */
>  	vma->vm_pgoff = 0;
>  
> @@ -36,8 +47,35 @@ static int qda_gem_mmap_obj(struct drm_gem_object *gem_obj, struct vm_area_struc
>  	return 0;
>  }
>  
> -static const struct drm_gem_object_funcs qda_gem_object_funcs = {
> +/*
> + * Lets another context bank map this buffer when a session imports the
> + * dma-buf this object was exported as: see qda_gem_prime_import().
> + */
> +static struct sg_table *qda_gem_get_sg_table(struct drm_gem_object *gem_obj)
> +{
[ ... ]
> +	return sgt;
> +}
> +
> +const struct drm_gem_object_funcs qda_gem_object_funcs = {
>  	.free = qda_gem_free_object,
> +	.get_sg_table = qda_gem_get_sg_table,
>  	.mmap = qda_gem_mmap_obj,
>  };

[Severity: High]
Does mmap() on a QDA GEM object or a PRIME-exported QDA DMA-BUF leak the
drm_gem_object reference and its coherent DMA buffer?

Both drm_gem_mmap_obj() (for DRM fd mmap) and
drm_gem_dmabuf_mmap()->drm_gem_prime_mmap() (for PRIME-exported DMA-BUF mmap,
now reachable because .get_sg_table is wired up) take a reference via
drm_gem_object_get(obj), set vma->vm_ops = obj->funcs->vm_ops, and call
obj->funcs->mmap(obj, vma).

Because qda_gem_object_funcs leaves .vm_ops unset and
qda_gem_mmap_obj()->qda_dma_mmap()->dma_mmap_coherent() does not set
vma->vm_ops either, vma->vm_ops remains NULL (and is replaced with
dummy_vm_ops in mmap_region()). When the VMA is later unmapped,
drm_gem_vm_close() is never called to drop the reference acquired at mmap
time, so qda_gem_free_object() never runs and qda_gem_obj and its DMA memory
are leaked.

Additionally, in the PRIME DMA-BUF mmap path, drm_gem_prime_mmap() adds
drm_vma_node_start(&obj->vma_node) to vma->vm_pgoff before calling
obj->funcs->mmap(). Resetting vma->vm_pgoff = 0 in qda_gem_mmap_obj() instead
of subtracting drm_vma_node_start(&gem_obj->vma_node) discards any non-zero
page offset passed to mmap() on the exported DMA-BUF fd.

Should qda_gem_object_funcs set .vm_ops to a vm_operations_struct with
.open = drm_gem_vm_open and .close = drm_gem_vm_close, and adjust
vma->vm_pgoff -= drm_vma_node_start(&gem_obj->vma_node) in qda_gem_mmap_obj()?

> diff --git a/drivers/accel/qda/qda_gem.h b/drivers/accel/qda/qda_gem.h
> index af3b9dfc75099..1dab494197aab 100644
> --- a/drivers/accel/qda/qda_gem.h
> +++ b/drivers/accel/qda/qda_gem.h
[ ... ]
> @@ -16,14 +17,20 @@ struct qda_session;
>  
>  /**
>   * struct qda_gem_obj - A buffer the DSP can access
> + *
> + * Either allocated by this driver or imported from another one. Use
> + * drm_gem_is_imported() on @base to tell which; imported buffers have no
> + * @virt and own no memory, they only hold a mapping of someone else's.
>   */
>  struct qda_gem_obj {
>  	/** @base: DRM GEM object base; must be first member */
>  	struct drm_gem_object base;
>  	/** @iommu_dev: Context bank this buffer is mapped into */
>  	struct qda_iommu_device *iommu_dev;
> -	/** @virt: Kernel virtual address of the buffer */
> +	/** @virt: Kernel virtual address, NULL for imported buffers */
>  	void *virt;
> +	/** @sgt: Mapping of an imported buffer, NULL otherwise */
> +	struct sg_table *sgt;
>  	/** @dma_addr: Address of the buffer as the DSP sees it */
>  	dma_addr_t dma_addr;

[Severity: Medium]
Can qda_gem_obj->dma_addr truncate the stream ID offset on 32-bit
CONFIG_COMPILE_TEST builds where dma_addr_t is 32 bits wide?

In qda_gem_prime_import_sg_table():

	qda_gem_obj->dma_addr = sg_dma_address(sgt->sgl) +
				qda_soc_sid_offset(qdev->soc_data, iommu_dev->sid);

qda_soc_sid_offset() returns a u64 with sid shifted left by 32 or 56 bits,
and qda_soc_dma_addr() takes a u64 iova, but struct qda_gem_obj stores
dma_addr as dma_addr_t. When !CONFIG_ARCH_DMA_ADDR_T_64BIT, storing the sum
into dma_addr_t drops the upper 32 bits containing the SID. Should dma_addr in
struct qda_gem_obj be u64?

> diff --git a/drivers/accel/qda/qda_prime.c b/drivers/accel/qda/qda_prime.c
> new file mode 100644
> index 0000000000000..7a709e3000d8b
> --- /dev/null
> +++ b/drivers/accel/qda/qda_prime.c
> @@ -0,0 +1,184 @@
[ ... ]
> +struct drm_gem_object *qda_gem_prime_import_sg_table(struct drm_device *drm_dev,
> +						     struct dma_buf_attachment *attach,
> +						     struct sg_table *sgt)
> +{
> +	struct qda_dev *qdev = qda_dev_from_drm(drm_dev);
> +	size_t size = PAGE_ALIGN(attach->dmabuf->size);
> +	struct qda_iommu_device *iommu_dev;
> +	struct qda_gem_obj *qda_gem_obj;
> +	int ret;
> +
> +	if (drm_prime_get_contiguous_size(sgt) < size) {
> +		drm_dbg_driver(drm_dev, "Rejecting non-contiguous buffer of %zu bytes\n", size);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	/*
> +	 * The buffer was mapped into the context bank of the session doing the
> +	 * import, so that is the bank the DSP has to be told about.
> +	 */
> +	iommu_dev = dev_get_drvdata(attach->dev);
> +	if (!iommu_dev)
> +		return ERR_PTR(-ENODEV);
> +
> +	qda_gem_obj = kzalloc_obj(*qda_gem_obj);
> +	if (!qda_gem_obj)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = drm_gem_object_init(drm_dev, &qda_gem_obj->base, size);
> +	if (ret) {
> +		kfree(qda_gem_obj);
> +		return ERR_PTR(ret);
> +	}

[Severity: Low]
Does an imported DMA-BUF GEM object need shmem backing via
drm_gem_object_init()?

drm_gem_object_init() calls drm_gem_private_object_init() and then allocates a
shmem file of size bytes via shmem_file_setup(). Since imported DMA-BUF
objects are backed by the exporter's memory rather than shmem (and also if
shmem_file_setup() fails, kfree(qda_gem_obj) is called without
drm_gem_private_object_fini() to clean up _resv and gpuva.lock), should
qda_gem_prime_import_sg_table() call drm_gem_private_object_init() instead?

> +
> +	qda_gem_obj->base.funcs = &qda_gem_object_funcs;
> +	qda_gem_obj->size = size;
> +	qda_gem_obj->sgt = sgt;
> +	qda_gem_obj->iommu_dev = iommu_dev;
> +	qda_gem_obj->dma_addr = sg_dma_address(sgt->sgl) +
> +				qda_soc_sid_offset(qdev->soc_data, iommu_dev->sid);
> +
> +	return &qda_gem_obj->base;
> +}
> +
[ ... ]
> +struct drm_gem_object *qda_gem_prime_import(struct drm_device *drm_dev,
> +					    struct dma_buf *dma_buf)
> +{
> +	struct qda_dev *qdev = qda_dev_from_drm(drm_dev);
> +	struct qda_session *session = qdev->importing_session;
> +	struct dma_buf_attachment *attach;
> +	struct drm_gem_object *obj;
> +	struct sg_table *sgt;
> +	int ret;
> +
> +	if (!session || !session->assigned_iommu_dev)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (drm_gem_is_prime_exported_dma_buf(drm_dev, dma_buf)) {
> +		/*
> +		 * A buffer this driver exported. The DRM core would hand back
> +		 * the original object, which stays in the context bank of the
> +		 * session that allocated it, while the DSP of this session
> +		 * translates addresses in its own bank. Share the object only
> +		 * within a bank, and map it again for any other.
> +		 */
> +		obj = dma_buf->priv;
> +		if (to_qda_gem_obj(obj)->iommu_dev == session->assigned_iommu_dev) {
> +			drm_gem_object_get(obj);
> +			return obj;
> +		}
> +	}

[Severity: High]
What happens if a session exports a QDA GEM object as a DMA-BUF (now enabled
via qda_gem_get_sg_table()) and then closes its DRM fd while the exported
dma_buf fd remains open?

When the exporting session closes its DRM fd:

  qda_postclose()
    -> qda_session_free()
      -> qda_memory_manager_release_device(session->assigned_iommu_dev)

qda_memory_manager_release_device() clears iommu_dev->assigned_session = NULL
and drops the session's kref on iommu_dev, even though the exported
qda_gem_obj is still alive and holds an unreferenced pointer in
qda_gem_obj->iommu_dev (and remains mapped in that context bank's SMMU
domain).

This appears to allow two failure modes:

1. If a second session opens the device, qda_memory_manager_assign_device()
can assign that same iommu_dev to the new session while the first session's
exported buffer is still mapped in that context bank. If the second session
then imports the dma_buf, to_qda_gem_obj(obj)->iommu_dev ==
session->assigned_iommu_dev in qda_gem_prime_import() evaluates to true and
returns the original non-imported GEM object (where drm_gem_is_imported(obj)
is false), allowing the second session to mmap() the buffer through QDA as well
as sharing the SMMU context bank before the old buffer is freed.

2. If the RPMsg device is removed after the exporting session closed its DRM
fd while the exported dma_buf is still held open:

  qda_rpmsg_remove()
    -> qda_cb_unpopulate()
      -> qda_cb_teardown_device()
        -> qda_memory_manager_unregister_device()

drops the remaining kref on iommu_dev and frees iommu_dev via kfree(). Any
subsequent import/attach calling qda_gem_get_sg_table()->qda_dma_get_sgtable()
or final release calling
qda_gem_free_object()->qda_memory_manager_free()->qda_dma_free() will then
dereference the freed qda_gem_obj->iommu_dev pointer.

Should each qda_gem_obj hold a reference on iommu_dev (and keep the context
bank from being reassigned until all GEM allocations/mappings in it have been
released)?

> +
> +	attach = dma_buf_attach(dma_buf, session->assigned_iommu_dev->dev);
> +	if (IS_ERR(attach))
> +		return ERR_CAST(attach);
[ ... ]
> +int qda_prime_fd_to_handle(struct drm_device *drm_dev, struct drm_file *file_priv,
> +			   int prime_fd, u32 *handle)
> +{
> +	struct qda_dev *qdev = qda_dev_from_drm(drm_dev);
> +	struct qda_session *session = file_priv->driver_priv;
> +	int ret;
> +
> +	ret = qda_memory_manager_assign_device(qdev->iommu_mgr, session);
> +	if (ret)
> +		return ret;
> +
> +	guard(mutex)(&qdev->import_lock);
> +
> +	qdev->importing_session = session;
> +	ret = drm_gem_prime_fd_to_handle(drm_dev, file_priv, prime_fd, handle);
> +	qdev->importing_session = NULL;
> +
> +	return ret;
> +}

[Severity: High]
Can qda_prime_fd_to_handle() and qda_gem_free_object() race with or access
freed context bank devices after qda_rpmsg_remove()?

In qda_rpmsg_remove():

  drm_dev_unplug(&qdev->drm_dev);
  qda_cb_unpopulate(qdev);
  qda_deinit_device(qdev);

First, drm_ioctl_kernel() only checks drm_dev_is_unplugged() at entry and does
not hold a drm_dev_enter()/drm_dev_exit() critical section. Because
qda_prime_fd_to_handle() does not guard its body with
drm_dev_enter()/drm_dev_exit(), drm_dev_unplug() will not wait for an
in-flight import to complete before qda_cb_unpopulate() destroys the context
bank platform devices and qda_deinit_device() frees qdev->iommu_mgr->devices
and destroys qdev->import_lock.

Second, in qda_gem_prime_import(), dma_buf_attach(dma_buf,
session->assigned_iommu_dev->dev) attaches the DMA-BUF to the context bank
child platform_device (&entry->pdev->dev) without holding a reference that
keeps the device registered. If qda_rpmsg_remove() runs while a user process
has an open session with imported (or allocated) GEM objects,
qda_cb_unpopulate() calls of_platform_device_destroy(&entry->pdev->dev, NULL),
which unregisters and frees entry->pdev. When the user process later closes
its fd, qda_gem_free_object() calls:

  drm_prime_gem_destroy(gem_obj, qda_gem_obj->sgt)
    -> dma_buf_unmap_attachment_unlocked()
      -> dma_unmap_sgtable(attach->dev, ...)

(or qda_dma_free() -> dma_free_coherent(gem_obj->iommu_dev->dev, ...)),
dereferencing the unregistered and freed context bank struct device.

Should qda_prime_fd_to_handle() use drm_dev_enter()/drm_dev_exit(), and should
the context bank device lifetime be tied to active GEM objects/attachments or
managed via drmm so it outlives open file descriptors and GEM objects?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102-v4-v4-0-88f2f24a5f0b@qti.qualcomm.com?part=7

  reply	other threads:[~2026-11-01 22:52 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 [this message]
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
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-192740@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.