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 <[email protected]> > > 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; > > +} >

