>-----Original Message-----
>From: dri-devel <dri-devel-bounces(a)lists.freedesktop.org> On Behalf Of
>Thomas Zimmermann
>Sent: Friday, January 8, 2021 4:43 AM
>To: sumit.semwal(a)linaro.org; christian.koenig(a)amd.com;
>airlied(a)redhat.com; daniel(a)ffwll.ch; maarten.lankhorst(a)linux.intel.com;
>mripard(a)kernel.org; kraxel(a)redhat.com; hdegoede(a)redhat.com;
>sean(a)poorly.run; eric(a)anholt.net; sam(a)ravnborg.org
>Cc: Daniel Vetter <daniel.vetter(a)ffwll.ch>; dri-devel(a)lists.freedesktop.org;
>virtualization(a)lists.linux-foundation.org; linaro-mm-sig(a)lists.linaro.org;
>Thomas Zimmermann <tzimmermann(a)suse.de>; linux-
>media(a)vger.kernel.org
>Subject: [PATCH v4 01/13] dma-buf: Add vmap_local and vnumap_local
>operations
>
>The existing dma-buf calls dma_buf_vmap() and dma_buf_vunmap() are
>allowed to pin the buffer or acquire the buffer's reservation object
>lock.
>
>This is a problem for callers that only require a short-term mapping
>of the buffer without the pinning, or callers that have special locking
>requirements. These may suffer from unnecessary overhead or interfere
>with regular pin operations.
>
>The new interfaces dma_buf_vmap_local(), dma_buf_vunmapo_local(), and
>their rsp callbacks in struct dma_buf_ops provide an alternative without
>pinning or reservation locking. Callers are responsible for these
>operations.
>
>v4:
> * update documentation (Daniel)
>
>Signed-off-by: Thomas Zimmermann <tzimmermann(a)suse.de>
>Reviewed-by: Daniel Vetter <daniel.vetter(a)ffwll.ch>
>Suggested-by: Daniel Vetter <daniel.vetter(a)ffwll.ch>
>---
> drivers/dma-buf/dma-buf.c | 81
>+++++++++++++++++++++++++++++++++++++++
> include/linux/dma-buf.h | 34 ++++++++++++++++
> 2 files changed, 115 insertions(+)
>
>diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
>index b8465243eca2..01f9c74d97fa 100644
>--- a/drivers/dma-buf/dma-buf.c
>+++ b/drivers/dma-buf/dma-buf.c
>@@ -1295,6 +1295,87 @@ void dma_buf_vunmap(struct dma_buf *dmabuf,
>struct dma_buf_map *map)
> }
> EXPORT_SYMBOL_GPL(dma_buf_vunmap);
>
>+/**
>+ * dma_buf_vmap_local - Create virtual mapping for the buffer object into
>kernel
>+ * address space.
>+ * @dmabuf: [in] buffer to vmap
>+ * @map: [out] returns the vmap pointer
>+ *
>+ * Unlike dma_buf_vmap() this is a short term mapping and will not pin
>+ * the buffer. The struct dma_resv for the @dmabuf must be locked until
>+ * dma_buf_vunmap_local() is called.
>+ *
>+ * Returns:
>+ * 0 on success, or a negative errno code otherwise.
>+ */
>+int dma_buf_vmap_local(struct dma_buf *dmabuf, struct dma_buf_map
>*map)
>+{
>+ struct dma_buf_map ptr;
>+ int ret = 0;
>+
>+ dma_buf_map_clear(map);
>+
>+ if (WARN_ON(!dmabuf))
>+ return -EINVAL;
>+
>+ dma_resv_assert_held(dmabuf->resv);
>+
>+ if (!dmabuf->ops->vmap_local)
>+ return -EINVAL;
You are clearing the map, and then doing the above checks.
Is it ok to change the map info and then exit on error?
Mike
>+ mutex_lock(&dmabuf->lock);
>+ if (dmabuf->vmapping_counter) {
>+ dmabuf->vmapping_counter++;
>+ BUG_ON(dma_buf_map_is_null(&dmabuf->vmap_ptr));
>+ *map = dmabuf->vmap_ptr;
>+ goto out_unlock;
>+ }
>+
>+ BUG_ON(dma_buf_map_is_set(&dmabuf->vmap_ptr));
>+
>+ ret = dmabuf->ops->vmap_local(dmabuf, &ptr);
>+ if (WARN_ON_ONCE(ret))
>+ goto out_unlock;
>+
>+ dmabuf->vmap_ptr = ptr;
>+ dmabuf->vmapping_counter = 1;
>+
>+ *map = dmabuf->vmap_ptr;
>+
>+out_unlock:
>+ mutex_unlock(&dmabuf->lock);
>+ return ret;
>+}
>+EXPORT_SYMBOL_GPL(dma_buf_vmap_local);
>+
>+/**
>+ * dma_buf_vunmap_local - Unmap a vmap obtained by
>dma_buf_vmap_local.
>+ * @dmabuf: [in] buffer to vunmap
>+ * @map: [in] vmap pointer to vunmap
>+ *
>+ * Release a mapping established with dma_buf_vmap_local().
>+ */
>+void dma_buf_vunmap_local(struct dma_buf *dmabuf, struct
>dma_buf_map *map)
>+{
>+ if (WARN_ON(!dmabuf))
>+ return;
>+
>+ dma_resv_assert_held(dmabuf->resv);
>+
>+ BUG_ON(dma_buf_map_is_null(&dmabuf->vmap_ptr));
>+ BUG_ON(dmabuf->vmapping_counter == 0);
>+ BUG_ON(!dma_buf_map_is_equal(&dmabuf->vmap_ptr, map));
>+
>+ mutex_lock(&dmabuf->lock);
>+ if (--dmabuf->vmapping_counter == 0) {
>+ if (dmabuf->ops->vunmap_local)
>+ dmabuf->ops->vunmap_local(dmabuf, map);
>+ dma_buf_map_clear(&dmabuf->vmap_ptr);
>+ }
>+ mutex_unlock(&dmabuf->lock);
>+}
>+EXPORT_SYMBOL_GPL(dma_buf_vunmap_local);
>+
> #ifdef CONFIG_DEBUG_FS
> static int dma_buf_debug_show(struct seq_file *s, void *unused)
> {
>diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
>index 628681bf6c99..aeed754b5467 100644
>--- a/include/linux/dma-buf.h
>+++ b/include/linux/dma-buf.h
>@@ -264,6 +264,38 @@ struct dma_buf_ops {
>
> int (*vmap)(struct dma_buf *dmabuf, struct dma_buf_map *map);
> void (*vunmap)(struct dma_buf *dmabuf, struct dma_buf_map
>*map);
>+
>+ /**
>+ * @vmap_local:
>+ *
>+ * Creates a virtual mapping for the buffer into kernel address space.
>+ *
>+ * This callback establishes short-term mappings for situations where
>+ * callers only use the buffer for a bounded amount of time; such as
>+ * updates to the framebuffer or reading back contained information.
>+ * In contrast to the regular @vmap callback, vmap_local does never
>pin
>+ * the buffer to a specific domain or acquire the buffer's reservation
>+ * lock.
>+ *
>+ * This is called with the &dma_buf.resv object locked. Callers must
>hold
>+ * the lock until after removing the mapping with @vunmap_local.
>+ *
>+ * This callback is optional.
>+ *
>+ * Returns:
>+ *
>+ * 0 on success or a negative error code on failure.
>+ */
>+ int (*vmap_local)(struct dma_buf *dmabuf, struct dma_buf_map
>*map);
>+
>+ /**
>+ * @vunmap_local:
>+ *
>+ * Removes a virtual mapping that was established by @vmap_local.
>+ *
>+ * This callback is optional.
>+ */
>+ void (*vunmap_local)(struct dma_buf *dmabuf, struct dma_buf_map
>*map);
> };
>
> /**
>@@ -501,4 +533,6 @@ int dma_buf_mmap(struct dma_buf *, struct
>vm_area_struct *,
> unsigned long);
> int dma_buf_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map);
> void dma_buf_vunmap(struct dma_buf *dmabuf, struct dma_buf_map
>*map);
>+int dma_buf_vmap_local(struct dma_buf *dmabuf, struct dma_buf_map
>*map);
>+void dma_buf_vunmap_local(struct dma_buf *dmabuf, struct
>dma_buf_map *map);
> #endif /* __DMA_BUF_H__ */
>--
>2.29.2
>
>_______________________________________________
>dri-devel mailing list
>dri-devel(a)lists.freedesktop.org
>https://lists.freedesktop.org/mailman/listinfo/dri-devel
It is observed 'use-after-free' on the dmabuf's file->f_inode with the
race between closing the dmabuf file and reading the dmabuf's debug
info.
Consider the below scenario where P1 is closing the dma_buf file
and P2 is reading the dma_buf's debug info in the system:
P1 P2
dma_buf_debug_show()
dma_buf_put()
__fput()
file->f_op->release()
dput()
....
dentry_unlink_inode()
iput(dentry->d_inode)
(where the inode is freed)
mutex_lock(&db_list.lock)
read 'dma_buf->file->f_inode'
(the same inode is freed by P1)
mutex_unlock(&db_list.lock)
dentry->d_op->d_release()-->
dma_buf_release()
.....
mutex_lock(&db_list.lock)
removes the dmabuf from the list
mutex_unlock(&db_list.lock)
In the above scenario, when dma_buf_put() is called on a dma_buf, it
first frees the dma_buf's file->f_inode(=dentry->d_inode) and then
removes this dma_buf from the system db_list. In between P2 traversing
the db_list tries to access this dma_buf's file->f_inode that was freed
by P1 which is a use-after-free case.
Since, __fput() calls f_op->release first and then later calls the
d_op->d_release, move the dma_buf's db_list removal from d_release() to
f_op->release(). This ensures that dma_buf's file->f_inode is not
accessed after it is released.
Cc: <stable(a)vger.kernel.org> # 5.4+
Fixes: 4ab59c3c638c ("dma-buf: Move dma_buf_release() from fops to dentry_ops")
Acked-by: Christian König <christian.koenig(a)amd.com>
Signed-off-by: Charan Teja Reddy <charante(a)codeaurora.org>
---
V2: Resending with stable tags and Acks
V1: https://lore.kernel.org/patchwork/patch/1360118/
drivers/dma-buf/dma-buf.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 0eb80c1..a14dcbb 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -76,10 +76,6 @@ static void dma_buf_release(struct dentry *dentry)
dmabuf->ops->release(dmabuf);
- mutex_lock(&db_list.lock);
- list_del(&dmabuf->list_node);
- mutex_unlock(&db_list.lock);
-
if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
dma_resv_fini(dmabuf->resv);
@@ -88,6 +84,22 @@ static void dma_buf_release(struct dentry *dentry)
kfree(dmabuf);
}
+static int dma_buf_file_release(struct inode *inode, struct file *file)
+{
+ struct dma_buf *dmabuf;
+
+ if (!is_dma_buf_file(file))
+ return -EINVAL;
+
+ dmabuf = file->private_data;
+
+ mutex_lock(&db_list.lock);
+ list_del(&dmabuf->list_node);
+ mutex_unlock(&db_list.lock);
+
+ return 0;
+}
+
static const struct dentry_operations dma_buf_dentry_ops = {
.d_dname = dmabuffs_dname,
.d_release = dma_buf_release,
@@ -413,6 +425,7 @@ static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file)
}
static const struct file_operations dma_buf_fops = {
+ .release = dma_buf_file_release,
.mmap = dma_buf_mmap_internal,
.llseek = dma_buf_llseek,
.poll = dma_buf_poll,
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a
member of the Code Aurora Forum, hosted by The Linux Foundation
It is observed 'use-after-free' on the dmabuf's file->f_inode with the
race between closing the dmabuf file and reading the dmabuf's debug
info.
Consider the below scenario where P1 is closing the dma_buf file
and P2 is reading the dma_buf's debug info in the system:
P1 P2
dma_buf_debug_show()
dma_buf_put()
__fput()
file->f_op->release()
dput()
....
dentry_unlink_inode()
iput(dentry->d_inode)
(where the inode is freed)
mutex_lock(&db_list.lock)
read 'dma_buf->file->f_inode'
(the same inode is freed by P1)
mutex_unlock(&db_list.lock)
dentry->d_op->d_release()-->
dma_buf_release()
.....
mutex_lock(&db_list.lock)
removes the dmabuf from the list
mutex_unlock(&db_list.lock)
In the above scenario, when dma_buf_put() is called on a dma_buf, it
first frees the dma_buf's file->f_inode(=dentry->d_inode) and then
removes this dma_buf from the system db_list. In between P2 traversing
the db_list tries to access this dma_buf's file->f_inode that was freed
by P1 which is a use-after-free case.
Since, __fput() calls f_op->release first and then later calls the
d_op->d_release, move the dma_buf's db_list removal from d_release() to
f_op->release(). This ensures that dma_buf's file->f_inode is not
accessed after it is released.
Fixes: 4ab59c3c638c ("dma-buf: Move dma_buf_release() from fops to dentry_ops")
Signed-off-by: Charan Teja Reddy <charante(a)codeaurora.org>
---
drivers/dma-buf/dma-buf.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 0eb80c1..a14dcbb 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -76,10 +76,6 @@ static void dma_buf_release(struct dentry *dentry)
dmabuf->ops->release(dmabuf);
- mutex_lock(&db_list.lock);
- list_del(&dmabuf->list_node);
- mutex_unlock(&db_list.lock);
-
if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
dma_resv_fini(dmabuf->resv);
@@ -88,6 +84,22 @@ static void dma_buf_release(struct dentry *dentry)
kfree(dmabuf);
}
+static int dma_buf_file_release(struct inode *inode, struct file *file)
+{
+ struct dma_buf *dmabuf;
+
+ if (!is_dma_buf_file(file))
+ return -EINVAL;
+
+ dmabuf = file->private_data;
+
+ mutex_lock(&db_list.lock);
+ list_del(&dmabuf->list_node);
+ mutex_unlock(&db_list.lock);
+
+ return 0;
+}
+
static const struct dentry_operations dma_buf_dentry_ops = {
.d_dname = dmabuffs_dname,
.d_release = dma_buf_release,
@@ -413,6 +425,7 @@ static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file)
}
static const struct file_operations dma_buf_fops = {
+ .release = dma_buf_file_release,
.mmap = dma_buf_mmap_internal,
.llseek = dma_buf_llseek,
.poll = dma_buf_poll,
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a
member of the Code Aurora Forum, hosted by The Linux Foundation
On Thu, Dec 17, 2020 at 4:31 AM <siyanteng01(a)gmail.com> wrote:
>
> From: siyanteng <siyanteng01(a)gmail.com>
>
> When building cma_heap the following error shows up:
>
> drivers/dma-buf/heaps/cma_heap.c:195:10: error: implicit declaration of function 'vmap'; did you mean 'kmap'? [-Werror=implicit-function-declaration]
> 195 | vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL);
> | ^~~~
> | kmap
>
> Use this include: linux-next/include/linux/vmalloc.h
>
> Signed-off-by: siyanteng <siyanteng01(a)gmail.com>
Thanks for submitting this! My apologies for the trouble!
We already have a similar patch queued here:
https://cgit.freedesktop.org/drm/drm-misc/commit/?h=drm-misc-next-fixes&id=…
so hopefully that will land upstream soon.
thanks again!
-john
Also try to clarify a bit when dma_buf_begin/end_cpu_access should
be called.
Signed-off-by: Daniel Vetter <daniel.vetter(a)intel.com>
Cc: Thomas Zimmermann <tzimmermann(a)suse.de>
Cc: Sumit Semwal <sumit.semwal(a)linaro.org>
Cc: "Christian König" <christian.koenig(a)amd.com>
Cc: linux-media(a)vger.kernel.org
Cc: linaro-mm-sig(a)lists.linaro.org
---
drivers/dma-buf/dma-buf.c | 20 ++++++++++++++------
include/linux/dma-buf.h | 25 +++++++++----------------
2 files changed, 23 insertions(+), 22 deletions(-)
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index e63684d4cd90..a12fdffa130f 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -1001,15 +1001,15 @@ EXPORT_SYMBOL_GPL(dma_buf_move_notify);
* vmalloc space might be limited and result in vmap calls failing.
*
* Interfaces::
+ *
* void \*dma_buf_vmap(struct dma_buf \*dmabuf)
* void dma_buf_vunmap(struct dma_buf \*dmabuf, void \*vaddr)
*
* The vmap call can fail if there is no vmap support in the exporter, or if
- * it runs out of vmalloc space. Fallback to kmap should be implemented. Note
- * that the dma-buf layer keeps a reference count for all vmap access and
- * calls down into the exporter's vmap function only when no vmapping exists,
- * and only unmaps it once. Protection against concurrent vmap/vunmap calls is
- * provided by taking the dma_buf->lock mutex.
+ * it runs out of vmalloc space. Note that the dma-buf layer keeps a reference
+ * count for all vmap access and calls down into the exporter's vmap function
+ * only when no vmapping exists, and only unmaps it once. Protection against
+ * concurrent vmap/vunmap calls is provided by taking the &dma_buf.lock mutex.
*
* - For full compatibility on the importer side with existing userspace
* interfaces, which might already support mmap'ing buffers. This is needed in
@@ -1098,6 +1098,11 @@ static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
* dma_buf_end_cpu_access(). Only when cpu access is braketed by both calls is
* it guaranteed to be coherent with other DMA access.
*
+ * This function will also wait for any DMA transactions tracked through
+ * implicit synchronization in &dma_buf.resv. For DMA transactions with explicit
+ * synchronization this function will only ensure cache coherency, callers must
+ * ensure synchronization with such DMA transactions on their own.
+ *
* Can return negative error values, returns 0 on success.
*/
int dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
@@ -1199,7 +1204,10 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap);
* This call may fail due to lack of virtual mapping address space.
* These calls are optional in drivers. The intended use for them
* is for mapping objects linear in kernel space for high use objects.
- * Please attempt to use kmap/kunmap before thinking about these interfaces.
+ *
+ * To ensure coherency users must call dma_buf_begin_cpu_access() and
+ * dma_buf_end_cpu_access() around any cpu access performed through this
+ * mapping.
*
* Returns 0 on success, or a negative errno code otherwise.
*/
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index cf72699cb2bc..7eca37c8b10c 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -183,24 +183,19 @@ struct dma_buf_ops {
* @begin_cpu_access:
*
* This is called from dma_buf_begin_cpu_access() and allows the
- * exporter to ensure that the memory is actually available for cpu
- * access - the exporter might need to allocate or swap-in and pin the
- * backing storage. The exporter also needs to ensure that cpu access is
- * coherent for the access direction. The direction can be used by the
- * exporter to optimize the cache flushing, i.e. access with a different
+ * exporter to ensure that the memory is actually coherent for cpu
+ * access. The exporter also needs to ensure that cpu access is coherent
+ * for the access direction. The direction can be used by the exporter
+ * to optimize the cache flushing, i.e. access with a different
* direction (read instead of write) might return stale or even bogus
* data (e.g. when the exporter needs to copy the data to temporary
* storage).
*
- * This callback is optional.
+ * Note that this is both called through the DMA_BUF_IOCTL_SYNC IOCTL
+ * command for userspace mappings established through @mmap, and also
+ * for kernel mappings established with @vmap.
*
- * FIXME: This is both called through the DMA_BUF_IOCTL_SYNC command
- * from userspace (where storage shouldn't be pinned to avoid handing
- * de-factor mlock rights to userspace) and for the kernel-internal
- * users of the various kmap interfaces, where the backing storage must
- * be pinned to guarantee that the atomic kmap calls can succeed. Since
- * there's no in-kernel users of the kmap interfaces yet this isn't a
- * real problem.
+ * This callback is optional.
*
* Returns:
*
@@ -216,9 +211,7 @@ struct dma_buf_ops {
*
* This is called from dma_buf_end_cpu_access() when the importer is
* done accessing the CPU. The exporter can use this to flush caches and
- * unpin any resources pinned in @begin_cpu_access.
- * The result of any dma_buf kmap calls after end_cpu_access is
- * undefined.
+ * undo anything else done in @begin_cpu_access.
*
* This callback is optional.
*
--
2.29.2