* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox