mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Matt Evans <matt@ozlabs.org>
To: Jani Nikula <jani.nikula@linux.intel.com>,
	Joonas Lahtinen <joonas.lahtinen@linux.intel.com>,
	Rodrigo Vivi <rodrigo.vivi@intel.com>,
	Tvrtko Ursulin <tursulin@ursulin.net>
Cc: "Zack Rusin" <zack.rusin@broadcom.com>,
	"Sumit Semwal" <sumit.semwal@linaro.org>,
	"Christian König" <christian.koenig@amd.com>,
	linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org,
	linaro-mm-sig@lists.linaro.org, linux-kernel@vger.kernel.org,
	"Alex Mastro" <amastro@fb.com>,
	"Alex Williamson" <alex@shazbot.org>
Subject: Re: [PATCH] dma-buf: Annul dmabuf->file on file release
Date: Mon, 12 Oct 2026 17:03:56 +0100	[thread overview]
Message-ID: <e2171fff-62af-4185-a815-6293bea71220@ozlabs.org> (raw)
In-Reply-To: <fd564691-ca79-4fcb-80a7-8328c71ab322@ozlabs.org>

Hi i915 folks,

On 08/10/2026 14:41, Matt Evans wrote:
> Hi Zack,
> 
> On 07/10/2026 16:24, Zack Rusin wrote:
>> On Tue, Oct 6, 2026 at 3:15 PM Matt Evans <matt@ozlabs.org> wrote:
>>>
>>> The dmabuf release path is split between file and dentry release:
>>> dma_buf_file_release() is called shortly before the file is freed, and
>>> dma_buf_release() calls an exporter's dmabuf->ops->release when the
>>> dentry is freed.  However, the dentry can outlive the file (for
>>> example, if opened with O_PATH), meaning .release might be called some
>>> time after the file is freed.
>>>
>>> This presents a window, when closing a DMABUF file, in which
>>> dmabuf->file points to freed memory yet .release op has not yet been
>>> called.
>>>
>>> For VFIO, if a buffer's .release has not been called it's considered
>>> still active and subject to move/cleanup.  If so, it attempts to
>>> get_file_active() on dmabuf->file: a file close with dentry held open
>>> will call this function with a stale pointer, a UAF.
>>>
>>> To make this pattern safe, set dmabuf->file to NULL in
>>> dma_buf_file_release() to reflect that the associated file is now dead
>>> even if the DMABUF is not yet gone.  A get_file_active() ... fput()
>>> sequence concurrent with a file close will only execute as one of:
>>>
>>>    - Gets the file before file_ref_put() (dmabuf->file valid)
>>>    - Observes dmabuf->file pointing to a file, but it's DEAD (no file)
>>>    - Observes dmabuf->file = NULL (no file)
>>>
>>> Originally, drivers could assume dmabuf->file was valid until .release
>>> was called from fops->release.  This assumption was no longer valid
>>> after 4ab59c3c638c6 ("dma-buf: Move dma_buf_release() from fops to
>>> dentry_ops"), which moved the callback to the dentry release (by which
>>> point the file might have been freed).  With this commit, drivers must
>>> still consider that dmabuf->file could be NULL before .release.
>>>
>>> Fixes: 4ab59c3c638c6 ("dma-buf: Move dma_buf_release() from fops to 
>>> dentry_ops")
>>> Signed-off-by: Matt Evans <matt@ozlabs.org>
>>> ---
>>>
>>> Hi,
>>>
>>> This issue was found (by Claude Opus 5.5) in the context of VFIO's
>>> DMABUF export path.  VFIO iterates live DMABUFs with a
>>> get_file_active()/fput() block, which now becomes safe if the file is
>>> closed (and memory freed!) yet DMABUF .release hasn't yet occurred.
>>>
>>> However, there are a couple of other places that directly use
>>> dmabuf->file and seem able to race a closing file (i.e. without holding
>>> the file reference)?  If this is so, they'd be a UAF today; with this
>>> patch that goes away, but instead of a stale pointer dmabuf->file could
>>> be NULL:
>>>
>>> 1. drivers/gpu/drm/vmwgfx/ttm_object.c:get_dma_buf_unless_doomed()
>>>
>>>    file_ref_get(&dmabuf->file->f_ref); on a non-refcounted DMABUF
>>
>> Yes, that's a real UAF today. vmwgfx relied on the exporter's
>> dma_buf_ops.release running inside __fput() before file_free(), which
>> stopped being true with 4ab59c3c638c and we missed it.
>>
>> But this patch on its own makes vmwgfx worse. In the ordinary case,
>> where closing the file drops the last dentry reference, a close racing
>> PRIME_HANDLE_TO_FD is safe today: ttm_prime_dmabuf_release() runs from
>> the same __fput() and blocks on prime->mutex, keeping the file
>> allocated, so file_ref_get() just fails on the dead refcount. With
>> this patch dmabuf->file is NULL in that window and we oops. So this
>> shouldn't go to stable without a vmwgfx fix.
>>
>> For vmwgfx we'd probably need this one line change in
>> drivers/gpu/drm/vmwgfx/ttm_object.c to accompany this patch:
>> @@ -471,7 +475,7 @@ void ttm_object_device_release(struct
>> ttm_object_device **p_tdev)
>>    */
>>   static bool __must_check get_dma_buf_unless_doomed(struct dma_buf 
>> *dmabuf)
>>   {
>> -     return file_ref_get(&dmabuf->file->f_ref);
>> +     return get_file_active(&dmabuf->file) != NULL;
>>   }
>>
>> under prime->mutex, which keeps the struct dma_buf alive. It needs the
>> NULL store from this patch to fix the existing UAF, since otherwise
>> the recheck can accept a recycled file through the unchanged stale
>> pointer.
> 
> Thanks for confirming!  Ah, so get_file_active() is good here, cheers. 
> I'll repost as a 2-part series to first include this in vmwgfx and 
> second add the anNULL.
> 
>>> The commit message of 90ee6ed776c0 ("fs: port files to file_ref") hints
>>> this might be more subtle than replacing it with a get_file() variant
>>> (so as to accept a NULL file *).
>>>
>>> 2. drivers/gpu/drm/i915/gvt/dmabuf.c:intel_vgpu_get_dmabuf()
>>>
>>>    gvt_dbg_dpy(... file_count(dmabuf->file) ...);
> 
> FWIW aside, I re-read this, and there does appear to be a ref held 
> (dma_buf_export() has just happened, and dma_buf_fd() generated) so 
> dmabuf->file is stable at that point.

I think I was mistaken here:  the reference is moved to the file table 
by dma_buf_fd(), and seems another thread could immediately close it.

If so, there's an opportunity for userspace to (after this fix) cause a 
NULL deref here.

I'm about to repost the series but am happy to include a oneliner third 
patch to mitigate this, if you feel appropriate.

Is file_count() still interesting here, i.e. do you expect it to be >1 
immediately after export?  If still required, reading 
file_count(dmabuf->file) before the dma_buf_fd() would be safe (printing 
it later at the existing place).


Matt

> 
>>> Respective vmwgfx & i915 maintainers, what is your view?
>>> Or, indeed, if anyone sees any other questionable uses of dmabuf->file.
>>>
>>> Thanks,
>>>
>>>
>>> Matt
>>>
>>>
>>>    drivers/dma-buf/dma-buf.c | 6 +++++-
>>>    1 file changed, 5 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
>>> index 4c9add51f9ef..726639130477 100644
>>> --- a/drivers/dma-buf/dma-buf.c
>>> +++ b/drivers/dma-buf/dma-buf.c
>>> @@ -193,11 +193,15 @@ static void dma_buf_release(struct dentry *dentry)
>>>
>>>    static int dma_buf_file_release(struct inode *inode, struct file 
>>> *file)
>>>    {
>>> +       struct dma_buf *dmabuf = file->private_data;
>>> +
>>>          if (!is_dma_buf_file(file))
>>>                  return -EINVAL;
>>>
>>> -       __dma_buf_list_del(file->private_data);
>>> +       __dma_buf_list_del(dmabuf);
>>>
>>> +       /* Must be observed by __get_file_rcu() before file_free() */
>>> +       smp_store_mb(dmabuf->file, NULL);
>>
>> I think Alex is right and dmabuf can be NULL here. An early if
>> (!dmabuf) return 0" will fix that.
> 
> Yep, on it.
> 
> Many thanks,
> 
> 
> Matt
> 


      reply	other threads:[~2026-10-12 16:04 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-06 19:15 Matt Evans
2026-10-07  7:51 ` Christian König
2026-10-07 14:29   ` Matt Evans
2026-10-07 14:46     ` Christian König
2026-10-07  9:19 ` Alex Mastro
2026-10-07 14:42   ` Matt Evans
2026-10-07 15:24 ` Zack Rusin
2026-10-08 13:41   ` Matt Evans
2026-10-12 16:03     ` Matt Evans [this message]

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=e2171fff-62af-4185-a815-6293bea71220@ozlabs.org \
    --to=matt@ozlabs.org \
    --cc=alex@shazbot.org \
    --cc=amastro@fb.com \
    --cc=christian.koenig@amd.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=jani.nikula@linux.intel.com \
    --cc=joonas.lahtinen@linux.intel.com \
    --cc=linaro-mm-sig@lists.linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=rodrigo.vivi@intel.com \
    --cc=sumit.semwal@linaro.org \
    --cc=tursulin@ursulin.net \
    --cc=zack.rusin@broadcom.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®