In the Linux kernel, the following vulnerability has been resolved:
f2fs: avoid reading already updated pages during GC
We found the following issue during fuzz testing:
page: refcount:3 mapcount:0 mapping:00000000b6e89c65 index:0x18b2dc pfn:0x161ba9 memcg:f8ffff800e269c00 aops:f2fsmetaaops ino:2 flags: 0x52880000000080a9(locked|waiters|uptodate|lru|private|zone=1|kasantag=0x4a) raw: 52880000000080a9 fffffffec6e17588 fffffffec0ccc088 a7ffff8067063618 raw: 000000000018b2dc 0000000000000009 00000003ffffffff f8ffff800e269c00 page dumped because: VMBUGONFOLIO(foliotestuptodate(folio)) pageowner tracks the page as allocated postallochook+0x58c/0x5ec prepnewpage+0x34/0x284 getpagefromfreelist+0x2dcc/0x2e8c allocpagesnoprof+0x280/0x76c folioallocnoprof+0x18/0xac filemapgetfolio+0x6bc/0xdc4 pagecachegetpage+0x3c/0x104 dogarbagecollect+0x5c78/0x77a4 f2fsgc+0xd74/0x25f0 gcthreadfunc+0xb28/0x2930 kthread+0x464/0x5d8 retfromfork+0x10/0x20 ------------[ cut here ]------------ kernel BUG at mm/filemap.c:1563! folioendread+0x140/0x168 f2fsfinishreadbio+0x5c4/0xb80 f2fsreadendio+0x64c/0x708 bioendio+0x85c/0x8c0 blkupdaterequest+0x690/0x127c scsiendrequest+0x9c/0xb8c scsiiocompletion+0xf0/0x250 scsifinishcommand+0x430/0x45c scsicomplete+0x178/0x6d4 blkmqcompleterequest+0xcc/0x104 scsidoneinternal+0x214/0x454 scsidone+0x24/0x34
which is similar to the problem reported by syzbot: https://syzkaller.appspot.com/bug?extid=3686758660f980b402dc
This case is consistent with the description in commit 9bf1a3f ("f2fs: avoid GC causing encrypted file corrupted"): Page 1 is moved from blkaddr A to blkaddr B by movedatablock, and after being written it is marked as uptodate. Then, Page 1 is moved from blkaddr B to blkaddr C, VMBUGONFOLIO was triggered in the endio initiated by radatablock.
There is no need to read Page 1 again from blkaddr B, since it has already been updated. Therefore, avoid initiating I/O in this case.
In the Linux kernel, the following vulnerability has been resolved:
f2fs: fix data loss caused by incorrect use of natentry flag
Data loss can occur when fsync is performed on a newly created file (before any checkpoint has been written) concurrently with a checkpoint operation. The scenario is as follows:
create & write & fsync 'file A' write checkpoint - f2fsdosyncfile // inline inode - f2fswriteinode // inode folio is dirty - f2fswritecheckpoint - f2fsflushmergedwrites - f2fssyncnodepages - f2fsflushnatentries - f2fsfsyncnodepages // no dirty node - f2fsneedinodeblockupdate // return false SPO and lost 'file A'
f2fsflushnatentries() sets the ISCHECKPOINTED and HASLASTFSYNC flags for the natentry, but this does not mean that the checkpoint has actually completed successfully. However, f2fsneedinodeblockupdate() checks these flags and incorrectly assumes that the checkpoint has finished.
The root cause is that the semantics of ISCHECKPOINTED and HASLASTFSYNC are only guaranteed after the checkpoint write fully completes.
This patch modifies f2fsneedinodeblockupdate() to acquire the sbi->nodewrite lock before reading the natentry flags, ensuring that once ISCHECKPOINTED and HASLASTFSYNC are observed to be set, the checkpoint operation has already completed.