From 512194b982fd0edbc1dcaa50fafad75b1be26d42 Mon Sep 17 00:00:00 2001 From: Nikolay Amiantov Date: Wed, 15 Apr 2026 07:28:19 +0000 Subject: [PATCH] fuse: fix stale page cache data race on file growth --- fs/fuse/inode.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 735abf426a06..20741869ac2f 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -334,10 +334,42 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, * extend local i_size without keeping userspace server in sync. So, * attr->size coming from server can be stale. We cannot trust it. */ - if (!(cache_mask & STATX_SIZE)) - i_size_write(inode, attr->size); + if (!(cache_mask & STATX_SIZE)) { + if (S_ISREG(inode->i_mode) && attr->size > oldsize) { + struct folio *folio; + pgoff_t index = oldsize >> PAGE_SHIFT; + + spin_unlock(&fi->lock); + folio = __filemap_get_folio(inode->i_mapping, index, + FGP_LOCK | FGP_CREAT, + mapping_gfp_mask(inode->i_mapping)); + if (!IS_ERR(folio)) { + spin_lock(&fi->lock); + if (!test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { + folio_clear_uptodate(folio); + i_size_write(inode, attr->size); + } + spin_unlock(&fi->lock); + + folio_unlock(folio); + folio_put(folio); + goto size_updated; + } + spin_lock(&fi->lock); + /* + * Folio alloc failed (ENOMEM). Recheck in case a + * write/truncate started while we dropped the lock. + */ + if (!test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) + i_size_write(inode, attr->size); + } else { + i_size_write(inode, attr->size); + } + } spin_unlock(&fi->lock); +size_updated: + if (!cache_mask && S_ISREG(inode->i_mode)) { bool inval = false; -- 2.47.0