* [PATCH 0/2] fuse: fix invalidate lock leaks on DAX truncate error paths
@ 2026-08-17 15:17 Baokun Li
2026-08-17 15:18 ` [PATCH 1/2] fuse: fix invalidate lock leak on setattr writeback failure Baokun Li
2026-08-17 15:18 ` [PATCH 2/2] fuse: fix invalidate lock leak on open O_TRUNC DAX failure Baokun Li
0 siblings, 2 replies; 3+ messages in thread
From: Baokun Li @ 2026-08-17 15:17 UTC (permalink / raw)
To: fuse-devel; +Cc: miklos, linux-fsdevel, linux-kernel, vgoyal, jefflexu
Baokun Li (2):
fuse: fix invalidate lock leak on setattr writeback failure
fuse: fix invalidate lock leak on open O_TRUNC DAX failure
fs/fuse/dir.c | 9 ++++-----
fs/fuse/file.c | 4 ++--
2 files changed, 6 insertions(+), 7 deletions(-)
--
2.43.7
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] fuse: fix invalidate lock leak on setattr writeback failure
2026-08-17 15:17 [PATCH 0/2] fuse: fix invalidate lock leaks on DAX truncate error paths Baokun Li
@ 2026-08-17 15:18 ` Baokun Li
2026-08-17 15:18 ` [PATCH 2/2] fuse: fix invalidate lock leak on open O_TRUNC DAX failure Baokun Li
1 sibling, 0 replies; 3+ messages in thread
From: Baokun Li @ 2026-08-17 15:18 UTC (permalink / raw)
To: fuse-devel; +Cc: miklos, linux-fsdevel, linux-kernel, vgoyal, jefflexu, stable
fuse_do_setattr() takes filemap_invalidate_lock() for a DAX truncate
(fault_blocked = true) and releases it at the out:/error: labels. But
when a writeback flush is also needed, a write_inode_now() failure
returns directly and leaks the lock, so any later fault or truncate on
the file stalls on the stale rwsem.
For example, truncate(2) on a setuid file reaches fuse_do_setattr()
with both ATTR_SIZE and ATTR_MODE set:
truncate(2)
└─ do_truncate()
├─ dentry_needs_remove_privs() # S_ISUID
└─ notify_change() # KILL_SUID -> ATTR_MODE
└─ fuse_setattr() # no killpriv:
│ # ia_valid |= ATTR_MODE
└─ fuse_do_setattr()
├─ filemap_invalidate_lock() # IS_DAX && is_truncate
└─ write_inode_now() # is_wb && ATTR_MODE
└─ if (err) # e.g. daemon -> -EIO
return err # <- lock leaked
Fix this by adding an unlock label that releases the lock before
returning the error, and use it for the fuse_dax_break_layouts()
failure path as well.
Fixes: 6ae330cad6ef ("virtiofs: serialize truncate/punch_hole and dax fault path")
Cc: <stable@vger.kernel.org> # v5.10+
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/fuse/dir.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 0e2a1039fa43..48763bc192f3 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -2161,10 +2161,8 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
filemap_invalidate_lock(mapping);
fault_blocked = true;
err = fuse_dax_break_layouts(inode, 0, -1);
- if (err) {
- filemap_invalidate_unlock(mapping);
- return err;
- }
+ if (err)
+ goto unlock;
}
if (attr->ia_valid & ATTR_OPEN) {
@@ -2191,7 +2189,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
ATTR_TIMES_SET)) {
err = write_inode_now(inode, true);
if (err)
- return err;
+ goto unlock;
fuse_set_nowrite(inode);
fuse_release_nowrite(inode);
@@ -2299,6 +2297,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+unlock:
if (fault_blocked)
filemap_invalidate_unlock(mapping);
return err;
--
2.43.7
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] fuse: fix invalidate lock leak on open O_TRUNC DAX failure
2026-08-17 15:17 [PATCH 0/2] fuse: fix invalidate lock leaks on DAX truncate error paths Baokun Li
2026-08-17 15:18 ` [PATCH 1/2] fuse: fix invalidate lock leak on setattr writeback failure Baokun Li
@ 2026-08-17 15:18 ` Baokun Li
1 sibling, 0 replies; 3+ messages in thread
From: Baokun Li @ 2026-08-17 15:18 UTC (permalink / raw)
To: fuse-devel; +Cc: miklos, linux-fsdevel, linux-kernel, vgoyal, jefflexu, stable
fuse_open() takes filemap_invalidate_lock() for a DAX truncate
(dax_truncate = true) and releases it before the out_inode_unlock
label. But when fuse_dax_break_layouts() fails, the goto
out_inode_unlock skips the unlock and leaks the rwsem, so any later
fault or truncate on the file stalls on the stale lock.
fuse_dax_break_layouts() can fail with -ERESTARTSYS when a signal
interrupts the wait for busy DAX pages to drain:
open("file", O_RDWR | O_TRUNC)
└─ fuse_open()
├─ filemap_invalidate_lock() # dax_truncate
└─ fuse_dax_break_layouts()
└─ dax_break_layout()
└─ wait_page_idle() # TASK_INTERRUPTIBLE
└─ fuse_wait_dax_page() # unlock, schedule, re-lock
└─ signal → -ERESTARTSYS
goto out_inode_unlock # <- lock leaked
Fix this by moving filemap_invalidate_unlock() below the label so
that all error paths release the lock, and rename the label to
out_unlock as it now covers more than just the inode lock.
Fixes: 2fdbb8dd0155 ("fuse: fix deadlock between atomic O_TRUNC and page invalidation")
Cc: <stable@vger.kernel.org> # v6.0+
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/fuse/file.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index ceada75310b8..e7b2a839f081 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -272,7 +272,7 @@ static int fuse_open(struct inode *inode, struct file *file)
filemap_invalidate_lock(inode->i_mapping);
err = fuse_dax_break_layouts(inode, 0, -1);
if (err)
- goto out_inode_unlock;
+ goto out_unlock;
}
if (is_wb_truncate || dax_truncate)
@@ -296,9 +296,9 @@ static int fuse_open(struct inode *inode, struct file *file)
else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
invalidate_inode_pages2(inode->i_mapping);
}
+out_unlock:
if (dax_truncate)
filemap_invalidate_unlock(inode->i_mapping);
-out_inode_unlock:
if (is_wb_truncate || dax_truncate)
inode_unlock(inode);
--
2.43.7
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-17 15:18 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-17 15:17 [PATCH 0/2] fuse: fix invalidate lock leaks on DAX truncate error paths Baokun Li
2026-08-17 15:18 ` [PATCH 1/2] fuse: fix invalidate lock leak on setattr writeback failure Baokun Li
2026-08-17 15:18 ` [PATCH 2/2] fuse: fix invalidate lock leak on open O_TRUNC DAX failure Baokun Li
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.