ext4: fix missing brelse() in ext4xattrinodedecrefall()
In the Linux kernel, the following vulnerability has been resolved:
ext4: don't set EXT4GETBLOCKSCONVERT when splitting before submitting I/O
When allocating blocks during within-EOF DIO and writeback with dioreadnolock enabled, EXT4GETBLOCKSPREIO was set to split an existing large unwritten extent. However, EXT4GETBLOCKSCONVERT was set when calling ext4splitconvertextents(), which may potentially result in stale data issues.
Assume we have an unwritten extent, and then DIO writes the second half.
[UUUUUUUUUUUUUUUU] on-disk extent U: unwritten extent [UUUUUUUUUUUUUUUU] extent status tree |<- ->| ----> dio write this range
First, ext4iomapalloc() call ext4mapblocks() with EXT4GETBLOCKSPREIO, EXT4GETBLOCKSUNWRITEXT and EXT4GETBLOCKSCREATE flags set. ext4mapblocks() find this extent and call ext4splitconvertextents() with EXT4GETBLOCKSCONVERT and the above flags set.
Then, ext4splitconvertextents() calls ext4splitextent() with EXT4EXTMAYZEROOUT, EXT4EXTMARKUNWRIT2 and EXT4EXTDATAVALID2 flags set, and it calls ext4splitextentat() to split the second half with EXT4EXTDATAVALID2, EXT4EXTMARKUNWRIT1, EXT4EXTMAYZEROOUT and EXT4EXTMARKUNWRIT2 flags set. However, ext4splitextentat() failed to insert extent since a temporary lack -ENOSPC. It zeroes out the first half but convert the entire on-disk extent to written since the EXT4EXTDATAVALID2 flag set, but left the second half as unwritten in the extent status tree.
[0000000000SSSSSS] data S: stale data, 0: zeroed [WWWWWWWWWWWWWWWW] on-disk extent W: written extent [WWWWWWWWWWUUUUUU] extent status tree
Finally, if the DIO failed to write data to the disk, the stale data in the second half will be exposed once the cached extent entry is gone.
Fix this issue by not passing EXT4GETBLOCKSCONVERT when splitting an unwritten extent before submitting I/O, and make ext4splitconvertextents() to zero out the entire extent range to zero for this case, and also mark the extent in the extent status tree for consistency.
In the Linux kernel, the following vulnerability has been resolved:
ext4: don't zero the entire extent if EXT4EXTDATAPARTIALVALID1
When allocating initialized blocks from a large unwritten extent, or when splitting an unwritten extent during end I/O and converting it to initialized, there is currently a potential issue of stale data if the extent needs to be split in the middle.
0 A B N [UUUUUUUUUUUU] U: unwritten extent [--DDDDDDDD--] D: valid data |<- ->| ----> this range needs to be initialized
ext4splitextent() first try to split this extent at B with EXT4EXTDATAENTIREVALID1 and EXT4EXTMAYZEROOUT flag set, but ext4splitextentat() failed to split this extent due to temporary lack of space. It zeroout B to N and mark the entire extent from 0 to N as written.
0 A B N [WWWWWWWWWWWW] W: written extent [SSDDDDDDDDZZ] Z: zeroed, S: stale data
ext4splitextent() then try to split this extent at A with EXT4EXTDATAVALID2 flag set. This time, it split successfully and left a stale written extent from 0 to A.
0 A B N [WW|WWWWWWWWWW] [SS|DDDDDDDDZZ]
Fix this by pass EXT4EXTDATAPARTIALVALID1 to ext4splitextentat() when splitting at B, don't convert the entire extent to written and left it as unwritten after zeroing out B to N. The remaining work is just like the standard two-part split. ext4splitextent() will pass the EXT4EXTDATAVALID2 flag when it calls ext4splitextentat() for the second time, allowing it to properly handle the split. If the split is successful, it will keep extent from 0 to A as unwritten.
In the Linux kernel, the following vulnerability has been resolved:
ext4: move ext4percpuparaminit() before ext4mbinit()
When running kvm-xfstests -c ext4/1k -C 1 generic/383 with the DOUBLECHECK macro defined, the following panic is triggered:
================================================================== EXT4-fs error (device vdc): ext4validateblockbitmap:423: comm mount: bg 0: bad block bitmap checksum BUG: unable to handle page fault for address: ff110000fa2cc000 PGD 3e01067 P4D 3e02067 PUD 0 Oops: Oops: 0000 [#1] SMP NOPTI CPU: 0 UID: 0 PID: 2386 Comm: mount Tainted: G W 6.18.0-gba65a4e7120a-dirty #1152 PREEMPT(none) RIP: 0010:percpucounteraddbatch+0x13/0xa0 Call Trace: <TASK> ext4markgroupbitmapcorrupted+0xcb/0xe0 ext4validateblockbitmap+0x2a1/0x2f0 ext4readblockbitmap+0x33/0x50 mbgroupbbbitmapalloc+0x33/0x80 ext4mbaddgroupinfo+0x190/0x250 ext4mbinitbackend+0x87/0x290 ext4mbinit+0x456/0x640 ext4fillsuper+0x1072/0x1680 ext4fillsuper+0xd3/0x280 gettreebdevflags+0x132/0x1d0 vfsgettree+0x29/0xd0 vfscmdcreate+0x59/0xe0 dosysfsconfig+0x4f6/0x6b0 dosyscall64+0x50/0x1f0 entrySYSCALL64afterhwframe+0x76/0x7e ==================================================================
This issue can be reproduced using the following commands: mkfs.ext4 -F -q -b 1024 /dev/sda 5G tune2fs -O quota,project /dev/sda mount /dev/sda /tmp/test
With DOUBLECHECK defined, mbgroupbbbitmapalloc() reads and validates the block bitmap. When the validation fails, ext4markgroupbitmapcorrupted() attempts to update sbi->sfreeclusterscounter. However, this percpucounter has not been initialized yet at this point, which leads to the panic described above.
Fix this by moving the execution of ext4percpuparaminit() to occur before ext4mbinit(), ensuring the per-CPU counters are initialized before they are used.
In the Linux kernel, the following vulnerability has been resolved:
ext4: fix iloc.bh leak in ext4fcreplayinode() error paths
During code review, Joseph found that ext4fcreplayinode() calls ext4getfcinodeloc() to get the inode location, which holds a reference to iloc.bh that must be released via brelse().
However, several error paths jump to the 'out' label without releasing iloc.bh:
- ext4handledirtymetadata() failure - syncdirtybuffer() failure - ext4markinodeused() failure - ext4iget() failure
Fix this by introducing an 'outbrelse' label placed just before the existing 'out' label to ensure iloc.bh is always released.
Additionally, make ext4fcreplayinode() propagate errors properly instead of always returning 0.
ext4: replace BUGON with proper error handling in ext4readinlinefolio