dmabuf: fix use-after-free of dmabuf's file->f_inode
Subsystems (2)
PATCH 1/1 dmabuf: fix use-after-free of dmabuf's file->f_inode
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.
--- drivers/dma-buf/dma-buf.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)
drivers/dma-buf/dma-buf.c +17 -4
@@ -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,
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
Am 04.01.21 um 12:36 schrieb Charan Teja Reddy:
Quoted 34 lines
It is observed 'use-after-free' on the dmabuf's
file->f_inodewith the race between closing the dmabuf file and reading the dmabuf's debug info.Consider the below scenario where P1 is closing the
dma_buffile 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 adma_buf, it first frees the dma_buf'sfile->f_inode(=dentry->d_inode)and then removes thisdma_buffrom the systemdb_list. In between P2 traversing thedb_listtries to access this dma_buf'sfile->f_inodethat was freed by P1 which is a use-after-free case.
Quoted 7 lines
Since,
__fput()callsf_op->releasefirst and then later calls thed_op->d_release, move the dma_buf'sdb_listremoval fromd_release()tof_op->release(). This ensures that dma_buf'sfile->f_inodeis 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 <[email protected]>
Not an expert on the debugfs stuff in DMA-buf, but the explanation sounds perfectly correct to me.
Quoted 43 lines
--- drivers/dma-buf/dma-buf.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)drivers/dma-buf/dma-buf.c +16
-4@@ -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,
drivers/dma-buf/dma-buf.c static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file) · line 431
Quoted 7 lines
drivers/dma-buf/dma-buf.c +1
-0@@ -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,
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
Sumit Semwal
maintainerHi Charan,
Quoted 41 lines · Christian König
Am 04.01.21 um 12:36 schrieb Charan Teja Reddy:
Quoted 39 lines
It is observed 'use-after-free' on the dmabuf's
file->f_inodewith the race between closing the dmabuf file and reading the dmabuf's debug info.Consider the below scenario where P1 is closing the
dma_buffile 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 adma_buf, it first frees the dma_buf'sfile->f_inode(=dentry->d_inode)and then removes thisdma_buffrom the systemdb_list. In between P2 traversing thedb_listtries to access this dma_buf'sfile->f_inodethat was freed by P1 which is a use-after-free case.Since,
__fput()callsf_op->releasefirst and then later calls thed_op->d_release, move the dma_buf'sdb_listremoval fromd_release()tof_op->release(). This ensures that dma_buf'sfile->f_inodeis not accessed after it is released.
Quoted 7 lines
Quoted 2 lines
Fixes 4ab59c3c638c ("dma-buf: Move dma_buf_release() from fops to dentry_ops")Signed-off-by Charan Teja Reddy <[email protected]>Not an expert on the debugfs stuff in DMA-buf, but the explanation sounds perfectly correct to me.
Acked-by Christian König <[email protected]>
Thanks for your fix; I will queue it up in the fixes branch. Can you please also send it to be queued to 5.4+ stable branches?
Quoted 46 lines
Quoted 46 lines
--- drivers/dma-buf/dma-buf.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)drivers/dma-buf/dma-buf.c +16
-4@@ -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) }
Quoted 6 lines
drivers/dma-buf/dma-buf.c static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file) · line 431
Quoted 5 lines
drivers/dma-buf/dma-buf.c +1
-0static 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,
Best,
Sumit.
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel