In the Linux kernel, the following vulnerability has been resolved:
ext4: validate pidx bounds in ext4extcorrectindexes
ext4extcorrectindexes() walks up the extent tree correcting index entries when the first extent in a leaf is modified. Before accessing path[k].pidx->eiblock, there is no validation that pidx falls within the valid range of index entries for that level.
If the on-disk extent header contains a corrupted or crafted ehentries value, pidx can point past the end of the allocated buffer, causing a slab-out-of-bounds read.
Fix this by validating path[k].pidx against EXTLASTINDEX() at both access sites: before the while loop and inside it. Return -EFSCORRUPTED if the index pointer is out of range, consistent with how other bounds violations are handled in the ext4 extent tree code.
ext4: reject mount if bigalloc with sfirstdatablock != 0
ext4: fix use-after-free in updatesuperwork when racing with umount
In the Linux kernel, the following vulnerability has been resolved:
ext4: fix e4b bitmap inconsistency reports
A bitmap inconsistency issue was observed during stress tests under mixed huge-page workloads. Ext4 reported multiple e4b bitmap check failures like:
ext4mbcomplexscangroup:2508: group 350, 8179 free clusters as per group info. But got 8192 blocks
Analysis and experimentation confirmed that the issue is caused by a race condition between page migration and bitmap modification. Although this timing window is extremely narrow, it is still hit in practice:
foliolock ext4mbloadbuddy migratefolio check ref count foliomccopy filemapgetfolio foliotryget(folio) ...... mbmarkused ext4mbunloadbuddy foliomigratemapping folioreffreeze foliounlock
The root cause of this issue is that the fast path of loadbuddy only increments the folio's reference count, which is insufficient to prevent concurrent folio migration. We observed that the folio migration process acquires the folio lock. Therefore, we can determine whether to take the fast path in loadbuddy by checking the lock status. If the folio is locked, we opt for the slow path (which acquires the lock) to close this concurrency window.
Additionally, this change addresses the following issues:
When the DOUBLECHECK macro is enabled to inspect bitmap-related issues, the following error may be triggered:
corruption in group 324 at byte 784(6272): f in copy != ff on disk/prealloc
Analysis reveals that this is a false positive. There is a specific race window where the bitmap and the group descriptor become momentarily inconsistent, leading to this error report:
ext4mbloadbuddy ext4mbloadbuddy filemapgetfolio(create|lock) foliolock ext4mbinitcache foliomarkuptodate filemapgetfolio(no lock) ...... mbmarkused mbmarkuseddouble mbcmpbitmaps mbsetbits(e4b->bdbitmap) foliounlock
The original logic assumed that since mbcmpbitmaps is called when the bitmap is newly loaded from disk, the folio lock would be sufficient to prevent concurrent access. However, this overlooks a specific race condition: if another process attempts to load buddy and finds the folio is already in an uptodate state, it will immediately begin using it without holding folio lock.
ext4: fix bounds check in checkxattrs() to prevent out-of-bounds access
In the Linux kernel, the following vulnerability has been resolved:
ext4: fix dirtyclusters double decrement on fs shutdown
fstests test generic/388 occasionally reproduces a warning in ext4putsuper() associated with the dirty clusters count:
WARNING: CPU: 7 PID: 76064 at fs/ext4/super.c:1324 ext4putsuper+0x48c/0x590 [ext4]
Tracing the failure shows that the warning fires due to an sdirtyclusterscounter value of -1. IOW, this appears to be a spurious decrement as opposed to some sort of leak. Further tracing of the dirty cluster count deltas and an LLM scan of the resulting output identified the cause as a double decrement in the error path between ext4mbmarkdiskspaceused() and the caller ext4mbnewblocks().
First, note that generic/388 is a shutdown vs. fsstress test and so produces a random set of operations and shutdown injections. In the problematic case, the shutdown triggers an error return from the ext4handledirtymetadata() call(s) made from ext4mbmarkcontext(). The changed value is non-zero at this point, so ext4mbmarkdiskspaceused() does not exit after the error bubbles up from ext4mbmarkcontext(). Instead, the former decrements both cluster counters and returns the error up to ext4mbnewblocks(). The latter falls into the !ar->len out path which decrements the dirty clusters counter a second time, creating the inconsistency.
To avoid this problem and simplify ownership of the cluster reservation in this codepath, lift the counter reduction to a single place in the caller. This makes it more clear that ext4mbnewblocks() is responsible for acquiring cluster reservation (via ext4claimfreeclusters()) in the !delalloc case as well as releasing it, regardless of whether it ends up consumed or returned due to failure.
In the Linux kernel, the following vulnerability has been resolved:
ext4: don't cache extent during splitting extent
Caching extents during the splitting process is risky, as it may result in stale extents remaining in the status tree. Moreover, in most cases, the corresponding extent block entries are likely already cached before the split happens, making caching here not particularly useful.
Assume we have an unwritten extent, and then DIO writes the first half.
[UUUUUUUUUUUUUUUU] on-disk extent U: unwritten extent [UUUUUUUUUUUUUUUU] extent status tree |<- ->| ----> dio write this range
First, when ext4splitextentat() splits this extent, it truncates the existing extent and then inserts a new one. During this process, this extent status entry may be shrunk, and calls to ext4findextent() and ext4cacheextents() may occur, which could potentially insert the truncated range as a hole into the extent status tree. After the split is completed, this hole is not replaced with the correct status.
[UUUUUUU|UUUUUUUU] on-disk extent U: unwritten extent [UUUUUUU|HHHHHHHH] extent status tree H: hole
Then, the outer calling functions will not correct this remaining hole extent either. Finally, if we perform a delayed buffer write on this latter part, it will re-insert the delayed extent and cause an error in space accounting.
In adition, if the unwritten extent cache is not shrunk during the splitting, ext4cacheextents() also conflicts with existing extents when caching extents. In the future, we will add checks when caching extents, which will trigger a warning. Therefore, Do not cache extents that are being split.