mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* fs_put_dax() vs. dax_holder_notify_failure() race
@ 2026-09-30 14:29 Miklos Szeredi
  2026-09-30 15:35 ` Darrick J. Wong
  0 siblings, 1 reply; 2+ messages in thread
From: Miklos Szeredi @ 2026-09-30 14:29 UTC (permalink / raw)
  To: Vishal Verma, Dave Jiang, Alison Schofield, nvdimm, linux-cxl
  Cc: fuse-devel, Carlos Maiolino, linux-xfs, linux-kernel, John Groves

During ->notify_failure() callback will dereference holder.  It may be
NULL at this point, resulting in an Oops, or it may be non-NULL but be
freed during that call, resulting in UAF.

This affects xfs at this point, but the fuse extent map patchset[1]
also adds dax failure handling and is affected by this issue.

I think this should be fixed in drivers/dax/super.c, since this will
be a problem in each user.

Thanks,
Miklos

[1] Report from sashiko (select Patch 8):

https://sashiko.dev/#/patchset/20260922061019.3320196-1-mszeredi%40redhat.com

Or here it is inline:

commit 1580a2f8ad3f103a20b6280aa39a01f8b0be2d3f
Author: Miklos Szeredi <mszeredi@redhat.com>

fuse: add support for opening dax device as backing

Add FUSE_BACKING_IS_DEV flag that allows opening a character device
(dax device) as a backing, in addition to regular files. Mark the
inode with S_DAX if FUSE_LOOKUP returns with FUSE_ATTR_DAX set.

> diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
> index 58dbdd17c1ef4..aa558a0c2e647 100644
> --- a/fs/fuse/backing.c
> +++ b/fs/fuse/backing.c
[ ... ]
> @@ -22,9 +23,16 @@ static void fuse_backing_free(struct fuse_backing *fb)
>  {
>   pr_debug("%s: fb=0x%p\n", __func__, fb);
>
> - if (fb->file)
> - fput(fb->file);
> - put_cred(fb->cred);
> + switch (fb->type) {
> + case FUSE_BACKING_PATH:
> + path_put(&fb->path);
> + put_cred(fb->cred);
> + break;
> +
> + case FUSE_BACKING_DAXDEV:
> + fs_put_dax(fb->dax_dev, fb);
> + break;
> + }
>   kfree_rcu(fb, rcu);

Does this code introduce a use-after-free race with a concurrent hardware
memory failure?

When a DAX-backed FUSE backing file is closed, fuse_backing_free() drops the
DAX device holder by calling fs_put_dax() and frees the backing object using
normal RCU via kfree_rcu().

Concurrently, a hardware memory failure can trigger dax_holder_notify_failure(),
which protects its read-side critical section with SRCU using dax_read_lock().

If dax_holder_notify_failure() reads dax_dev->holder_ops and checks for NULL,
and is preempted here, fs_put_dax() can run to completion. This would set
dax_dev->holder_ops and dax_dev->holder_data to NULL, and eventually free fb
after a normal RCU grace period.

Because kfree_rcu() does not wait for SRCU readers, the memory could be freed
while dax_holder_notify_failure() is still running.

[ ... ]
> @@ -79,23 +87,88 @@ static struct fuse_backing *fuse_backing_id_remove(struct fuse_conn *fc, u64 id,
>   return fb;
>  }
>
> +static int fuse_dax_notify_failure(struct dax_device *daxdev, u64 offset, u64 len, int mf_flags)
> +{
> + struct fuse_backing *fb = dax_holder(daxdev);
> +
> + fb->dax_error = true;

Can this code cause a NULL pointer dereference or use-after-free when racing
with fuse_backing_free()?

When dax_holder_notify_failure() resumes after the race described in
fuse_backing_free(), it blindly calls ops->notify_failure() which is this
function.

Because fs_put_dax() may have already cleared dax_dev->holder_data,
dax_holder() might return NULL causing a NULL pointer dereference here.
Depending on the timing, it could also return a stale pointer to the freed
fb object, resulting in a use-after-free.

> +
> + return 0;
> +}

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: fs_put_dax() vs. dax_holder_notify_failure() race
  2026-09-30 14:29 fs_put_dax() vs. dax_holder_notify_failure() race Miklos Szeredi
@ 2026-09-30 15:35 ` Darrick J. Wong
  0 siblings, 0 replies; 2+ messages in thread
From: Darrick J. Wong @ 2026-09-30 15:35 UTC (permalink / raw)
  To: Miklos Szeredi
  Cc: Vishal Verma, Dave Jiang, Alison Schofield, nvdimm, linux-cxl,
	fuse-devel, Carlos Maiolino, linux-xfs, linux-kernel,
	John Groves

On Wed, Sep 30, 2026 at 04:29:33PM +0200, Miklos Szeredi wrote:
> During ->notify_failure() callback will dereference holder.  It may be
> NULL at this point, resulting in an Oops, or it may be non-NULL but be
> freed during that call, resulting in UAF.
> 
> This affects xfs at this point, but the fuse extent map patchset[1]
> also adds dax failure handling and is affected by this issue.
> 
> I think this should be fixed in drivers/dax/super.c, since this will
> be a problem in each user.

Yes, I would have thought that the notification code would grab a
refcount on the dax device so that it can't go away.  Though it does
seem a little wild that there's no means to pause fs_dax_put until
all the notifications have cleared....?

--D

> Thanks,
> Miklos
> 
> [1] Report from sashiko (select Patch 8):
> 
> https://sashiko.dev/#/patchset/20260922061019.3320196-1-mszeredi%40redhat.com
> 
> Or here it is inline:
> 
> commit 1580a2f8ad3f103a20b6280aa39a01f8b0be2d3f
> Author: Miklos Szeredi <mszeredi@redhat.com>
> 
> fuse: add support for opening dax device as backing
> 
> Add FUSE_BACKING_IS_DEV flag that allows opening a character device
> (dax device) as a backing, in addition to regular files. Mark the
> inode with S_DAX if FUSE_LOOKUP returns with FUSE_ATTR_DAX set.
> 
> > diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
> > index 58dbdd17c1ef4..aa558a0c2e647 100644
> > --- a/fs/fuse/backing.c
> > +++ b/fs/fuse/backing.c
> [ ... ]
> > @@ -22,9 +23,16 @@ static void fuse_backing_free(struct fuse_backing *fb)
> >  {
> >   pr_debug("%s: fb=0x%p\n", __func__, fb);
> >
> > - if (fb->file)
> > - fput(fb->file);
> > - put_cred(fb->cred);
> > + switch (fb->type) {
> > + case FUSE_BACKING_PATH:
> > + path_put(&fb->path);
> > + put_cred(fb->cred);
> > + break;
> > +
> > + case FUSE_BACKING_DAXDEV:
> > + fs_put_dax(fb->dax_dev, fb);
> > + break;
> > + }
> >   kfree_rcu(fb, rcu);
> 
> Does this code introduce a use-after-free race with a concurrent hardware
> memory failure?
> 
> When a DAX-backed FUSE backing file is closed, fuse_backing_free() drops the
> DAX device holder by calling fs_put_dax() and frees the backing object using
> normal RCU via kfree_rcu().
> 
> Concurrently, a hardware memory failure can trigger dax_holder_notify_failure(),
> which protects its read-side critical section with SRCU using dax_read_lock().
> 
> If dax_holder_notify_failure() reads dax_dev->holder_ops and checks for NULL,
> and is preempted here, fs_put_dax() can run to completion. This would set
> dax_dev->holder_ops and dax_dev->holder_data to NULL, and eventually free fb
> after a normal RCU grace period.
> 
> Because kfree_rcu() does not wait for SRCU readers, the memory could be freed
> while dax_holder_notify_failure() is still running.
> 
> [ ... ]
> > @@ -79,23 +87,88 @@ static struct fuse_backing *fuse_backing_id_remove(struct fuse_conn *fc, u64 id,
> >   return fb;
> >  }
> >
> > +static int fuse_dax_notify_failure(struct dax_device *daxdev, u64 offset, u64 len, int mf_flags)
> > +{
> > + struct fuse_backing *fb = dax_holder(daxdev);
> > +
> > + fb->dax_error = true;
> 
> Can this code cause a NULL pointer dereference or use-after-free when racing
> with fuse_backing_free()?
> 
> When dax_holder_notify_failure() resumes after the race described in
> fuse_backing_free(), it blindly calls ops->notify_failure() which is this
> function.
> 
> Because fs_put_dax() may have already cleared dax_dev->holder_data,
> dax_holder() might return NULL causing a NULL pointer dereference here.
> Depending on the timing, it could also return a stale pointer to the freed
> fb object, resulting in a use-after-free.
> 
> > +
> > + return 0;
> > +}
> 

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-30 15:35 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-30 14:29 fs_put_dax() vs. dax_holder_notify_failure() race Miklos Szeredi
2026-09-30 15:35 ` Darrick J. Wong

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®