[PATCH OLK-6.6] smb: client: zero the post-EOF pagecache on writeback
hulk inclusion category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18925 -------------------------------- The backport of commit 62584fd709e0 ("smb: client: clear post-EOF pagecache when extending a file via truncate") replaced the upstream dirty-state test in cifs_clear_stale_post_isize() with an unconditional PageUptodate() test, because this tree's cifs writeback only clamps a write to i_size and never zeroes the post-EOF tail. That keeps the original mmap-past-EOF bug fixed, but it also zeroes a clean folio whose post-EOF tail holds valid data cached from the server by a read: cifs_readpage_worker() asks the server for a full PAGE_SIZE and only zeroes the short-read part, so a read of the last local page can cache server data beyond a stale local i_size. If the file is then resized to a size inside that region, the zeroing discards valid data from the pagecache; for a writable mapping the zeros can later be written back to the server. Restore the upstream invariant instead: zero the tail of a dirty folio that straddles i_size when it is written back, as netfs_write_folio() does upstream, and then restore the dirty-state test. Fixes: 62584fd709e0 ("smb: client: clear post-EOF pagecache when extending a file via truncate") Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- fs/smb/client/file.c | 23 ++++++++++++++++++++++- fs/smb/client/inode.c | 16 ++++++---------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index c5ed161f17a9..99364442a887 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -2795,12 +2795,18 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) kunmap(page); return 0; /* don't care */ } /* check to make sure that we are not extending the file */ - if (mapping->host->i_size - offset < (loff_t)to) + if (mapping->host->i_size - offset < (loff_t)to) { to = (unsigned)(mapping->host->i_size - offset); + /* + * Zero the post-EOF tail to avoid later extension expose + * the stale bytes. + */ + zero_user_segment(page, to, PAGE_SIZE); + } rc = cifs_get_writable_file(CIFS_I(mapping->host), FIND_WR_ANY, &open_file); if (!rc) { bytes_written = cifs_write(open_file, open_file->pid, @@ -2843,10 +2849,11 @@ static void cifs_extend_writeback(struct address_space *mapping, unsigned int nr_pages; pgoff_t index = (start + *_len) / PAGE_SIZE; size_t len; bool stop = true; unsigned int i; + unsigned long long fpos; folio_batch_init(&batch); do { /* Firstly, we gather up a batch of contiguous dirty pages @@ -2930,10 +2937,17 @@ static void cifs_extend_writeback(struct address_space *mapping, if (!folio_batch_count(&batch)) break; for (i = 0; i < folio_batch_count(&batch); i++) { folio = batch.folios[i]; + fpos = folio_pos(folio); + + /* This folio straddles i_size: zero its tail. */ + if (fpos < i_size && i_size - fpos < folio_size(folio)) + folio_zero_segment(folio, i_size - fpos, + folio_size(folio)); + /* The folio should be locked, dirty and not undergoing * writeback from the loop above. */ if (!folio_clear_dirty_for_io(folio)) WARN_ON(1); @@ -3036,10 +3050,17 @@ static ssize_t cifs_write_back_from_locked_folio(struct address_space *mapping, i_size); } } len = min_t(unsigned long long, len, i_size - start); + /* + * If this folio straddles i_size, zero its post-EOF + * tail to avoid later extension expose the stale bytes. + */ + if (start < i_size && len < folio_size(folio)) + folio_zero_segment(folio, len, folio_size(folio)); + /* We now have a contiguous set of dirty pages, each with writeback * set; the first page is still locked at this point, but all the rest * have been unlocked. */ folio_unlock(folio); diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 32b527cc7b2d..99c246420837 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2917,19 +2917,15 @@ int cifs_truncate_page(struct address_space *mapping, loff_t from) * data as pages wholly beyond the EOF can't be faulted in, so the zeroing * is limited to that folio. The folio is zeroed rather than dropped so * that a concurrent extending write can't lose data. * * For callers that hold i_rwsem exclusively across the whole resize and - * have already updated i_size to @to, no genuine concurrent buffered writer - * can be racing, a lockless stat() adopting a server-confirmed size mid-resize - * has no data behind it and never dirties the folio, so it can't fool this - * check into skipping the zeroing. - * - * Do not use the folio's dirty state as the staleness criterion here: unlike - * netfs writeback, this tree's cifs writeback does not zero the part of a - * folio beyond i_size, so a folio cleaned by the setattr-side - * filemap_write_and_wait() can still hold stale bytes. + * have already updated i_size to @to, staleness is decided from the folio's + * dirty state instead: since no genuine concurrent buffered writer can be + * racing, a lockless stat() adopting a server-confirmed size mid-resize has + * no data behind it and never dirties the folio, so it can't fool this check + * into skipping the zeroing. * * pagecache_isize_extended() can't be reused here: it is keyed on a * sub-page block size (a no-op when the block size is >= PAGE_SIZE, as on * network filesystems), runs after i_size is updated, and doesn't wait for * writeback. Keep the two in sync if either is changed. @@ -2952,11 +2948,11 @@ static void cifs_clear_stale_post_isize(struct inode *inode, folio_wait_writeback(folio); if (folio_mkclean(folio)) folio_mark_dirty(folio); - if (folio_test_uptodate(folio)) { + if (folio_test_uptodate(folio) && folio_test_dirty(folio)) { unsigned long long fpos = folio_pos(folio); if (from < to && from < fpos + folio_size(folio)) { size_t end = umin(to - fpos, folio_size(folio)); size_t offset = from - fpos; -- 2.52.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/29981 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/3KT... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/29981 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/3KT...
participants (2)
-
patchwork bot -
Zizhi Wo