In the Linux kernel, the following vulnerability has been resolved:
ext4: fix race condition between ext4write and ext4convertinlinedata
Hulk Robot reported a BUGON: ================================================================== EXT4-fs error (device loop3): ext4mbgeneratebuddy:805: group 0, block bitmap and bg descriptor inconsistent: 25 vs 31513 free clusters kernel BUG at fs/ext4/ext4jbd2.c:53! invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 0 PID: 25371 Comm: syz-executor.3 Not tainted 5.10.0+ #1 RIP: 0010:ext4putnojournal fs/ext4/ext4jbd2.c:53 [inline] RIP: 0010:_ext4journalstop+0x10e/0x110 fs/ext4/ext4jbd2.c:116 [...] Call Trace: ext4writeinlinedataend+0x59a/0x730 fs/ext4/inline.c:795 genericperformwrite+0x279/0x3c0 mm/filemap.c:3344 ext4bufferedwriteiter+0x2e3/0x3d0 fs/ext4/file.c:270 ext4filewriteiter+0x30a/0x11c0 fs/ext4/file.c:520 doiterreadvwritev+0x339/0x3c0 fs/readwrite.c:732 doiterwrite+0x107/0x430 fs/readwrite.c:861 vfswritev fs/readwrite.c:934 [inline] dopwritev+0x1e5/0x380 fs/read_write.c:1031 [...] ==================================================================
Above issue may happen as follows: cpu1 cpu2 _|_ dopwritev vfswritev doiterwrite ext4filewriteiter ext4bufferedwriteiter genericperformwrite ext4dawritebegin vfsfallocate ext4fallocate ext4convertinlinedata ext4convertinlinedatanolock ext4destroyinlinedatanolock clear EXT4STATEMAYINLINEDATA ext4mapblocks ext4extmapblocks ext4mbnewblocks ext4mbregularallocator ext4mbgoodgroupnolock ext4mbinitgroup ext4mbinitcache ext4mbgeneratebuddy --> error ext4testinodestate(inode, EXT4STATEMAYINLINEDATA) ext4restoreinlinedata set EXT4STATEMAYINLINEDATA ext4blockwritebegin ext4dawriteend ext4testinodestate(inode, EXT4STATEMAYINLINEDATA) ext4writeinlinedataend handle=NULL ext4journalstop(handle) _ext4journalstop ext4putnojournal(handle) refcnt = (unsigned long)handle BUGON(refcnt == 0) ---> BUGON
The lock held by ext4convertinlinedata is xattrsem, but the lock held by genericperformwrite is i_rwsem. Therefore, the two locks can be concurrent.
To solve above issue, we add inodelock() for ext4convertinlinedata(). At the same time, move ext4convertinlinedata() in front of ext4punchhole(), remove similar handling from ext4punch_hole().