Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

f2fs: fix to zero post-eof page

fstest reports a f2fs bug:

generic/363 42s ... [failed, exit status 1]- output mismatch (see /share/git/fstests/results//generic/363.out.bad)
--- tests/generic/363.out 2025-01-12 21:57:40.271440542 +0800
+++ /share/git/fstests/results//generic/363.out.bad 2025-05-19 19:55:58.000000000 +0800
@@ -1,2 +1,78 @@
QA output created by 363
fsx -q -S 0 -e 1 -N 100000
+READ BAD DATA: offset = 0xd6fb, size = 0xf044, fname = /mnt/f2fs/junk
+OFFSET GOOD BAD RANGE
+0x1540d 0x0000 0x2a25 0x0
+operation# (mod 256) for the bad data may be 37
+0x1540e 0x0000 0x2527 0x1
...
(Run 'diff -u /share/git/fstests/tests/generic/363.out /share/git/fstests/results//generic/363.out.bad' to see the entire diff)
Ran: generic/363
Failures: generic/363
Failed 1 of 1 tests

The root cause is user can update post-eof page via mmap [1], however, f2fs
missed to zero post-eof page in below operations, so, once it expands i_size,
then it will include dummy data locates previous post-eof page, so during
below operations, we need to zero post-eof page.

Operations which can include dummy data after previous i_size after expanding
i_size:
- write
- mapwrite [1]
- truncate
- fallocate
* preallocate
* zero_range
* insert_range
* collapse_range
- clone_range (doesn’t support in f2fs)
- copy_range (doesn’t support in f2fs)

[1] https://man7.org/linux/man-pages/man2/mmap.2.html 'BUG section'

Cc: stable@kernel.org
Signed-off-by: Chao Yu <chao@kernel.org>
Reviewed-by: Zhiguo Niu <zhiguo.niu@unisoc.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

authored by

Chao Yu and committed by
Jaegeuk Kim
ba8dac35 6dea74e4

+38
+38
fs/f2fs/file.c
··· 35 35 #include <trace/events/f2fs.h> 36 36 #include <uapi/linux/f2fs.h> 37 37 38 + static void f2fs_zero_post_eof_page(struct inode *inode, loff_t new_size) 39 + { 40 + loff_t old_size = i_size_read(inode); 41 + 42 + if (old_size >= new_size) 43 + return; 44 + 45 + /* zero or drop pages only in range of [old_size, new_size] */ 46 + truncate_pagecache(inode, old_size); 47 + } 48 + 38 49 static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf) 39 50 { 40 51 struct inode *inode = file_inode(vmf->vma->vm_file); ··· 114 103 115 104 f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); 116 105 106 + filemap_invalidate_lock(inode->i_mapping); 107 + f2fs_zero_post_eof_page(inode, (folio->index + 1) << PAGE_SHIFT); 108 + filemap_invalidate_unlock(inode->i_mapping); 109 + 117 110 file_update_time(vmf->vma->vm_file); 118 111 filemap_invalidate_lock_shared(inode->i_mapping); 112 + 119 113 folio_lock(folio); 120 114 if (unlikely(folio->mapping != inode->i_mapping || 121 115 folio_pos(folio) > i_size_read(inode) || ··· 1125 1109 f2fs_down_write(&fi->i_gc_rwsem[WRITE]); 1126 1110 filemap_invalidate_lock(inode->i_mapping); 1127 1111 1112 + if (attr->ia_size > old_size) 1113 + f2fs_zero_post_eof_page(inode, attr->ia_size); 1128 1114 truncate_setsize(inode, attr->ia_size); 1129 1115 1130 1116 if (attr->ia_size <= old_size) ··· 1244 1226 ret = f2fs_convert_inline_inode(inode); 1245 1227 if (ret) 1246 1228 return ret; 1229 + 1230 + filemap_invalidate_lock(inode->i_mapping); 1231 + f2fs_zero_post_eof_page(inode, offset + len); 1232 + filemap_invalidate_unlock(inode->i_mapping); 1247 1233 1248 1234 pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; 1249 1235 pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; ··· 1532 1510 f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); 1533 1511 filemap_invalidate_lock(inode->i_mapping); 1534 1512 1513 + f2fs_zero_post_eof_page(inode, offset + len); 1514 + 1535 1515 f2fs_lock_op(sbi); 1536 1516 f2fs_drop_extent_tree(inode); 1537 1517 truncate_pagecache(inode, offset); ··· 1654 1630 ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1); 1655 1631 if (ret) 1656 1632 return ret; 1633 + 1634 + filemap_invalidate_lock(mapping); 1635 + f2fs_zero_post_eof_page(inode, offset + len); 1636 + filemap_invalidate_unlock(mapping); 1657 1637 1658 1638 pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; 1659 1639 pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; ··· 1790 1762 /* avoid gc operation during block exchange */ 1791 1763 f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); 1792 1764 filemap_invalidate_lock(mapping); 1765 + 1766 + f2fs_zero_post_eof_page(inode, offset + len); 1793 1767 truncate_pagecache(inode, offset); 1794 1768 1795 1769 while (!ret && idx > pg_start) { ··· 1848 1818 err = f2fs_convert_inline_inode(inode); 1849 1819 if (err) 1850 1820 return err; 1821 + 1822 + filemap_invalidate_lock(inode->i_mapping); 1823 + f2fs_zero_post_eof_page(inode, offset + len); 1824 + filemap_invalidate_unlock(inode->i_mapping); 1851 1825 1852 1826 f2fs_balance_fs(sbi, true); 1853 1827 ··· 4894 4860 err = file_modified(file); 4895 4861 if (err) 4896 4862 return err; 4863 + 4864 + filemap_invalidate_lock(inode->i_mapping); 4865 + f2fs_zero_post_eof_page(inode, iocb->ki_pos + iov_iter_count(from)); 4866 + filemap_invalidate_unlock(inode->i_mapping); 4897 4867 return count; 4898 4868 } 4899 4869