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

