VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-45942

CVE-2026-45942

Description

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:

ext4_mb_complex_scan_group: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:

folio_lock ext4_mb_load_buddy __migrate_folio check ref count folio_mc_copy __filemap_get_folio folio_try_get(folio) ...... mb_mark_used ext4_mb_unload_buddy __folio_migrate_mapping folio_ref_freeze folio_unlock

The root cause of this issue is that the fast path of load_buddy 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 load_buddy 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 DOUBLE_CHECK 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:

ext4_mb_load_buddy ext4_mb_load_buddy __filemap_get_folio(create|lock) folio_lock ext4_mb_init_cache folio_mark_uptodate __filemap_get_folio(no lock) ...... mb_mark_used mb_mark_used_double mb_cmp_bitmaps mb_set_bits(e4b->bd_bitmap) folio_unlock

The original logic assumed that since mb_cmp_bitmaps 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.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Race condition in ext4 buddy allocator between page migration and bitmap modification causes inconsistent free block counts and false corruption warnings.

Vulnerability

Race condition in ext4_mb_load_buddy function between page migration and bitmap modification. The fast path only increments the folio reference count, which does not prevent concurrent migration. When the folio lock is held by migration, the fast path can proceed with a stale bitmap. Affected Linux kernel versions before the stable commit c05033cfc5c7.

Exploitation

The race window is extremely narrow but can be triggered under mixed huge-page workloads. An attacker needs to cause concurrent page migration and ext4 buddy allocation operations. No special authentication required, only ability to create workloads that trigger both paths.

Impact

The inconsistency leads to false positive error messages from ext4_mb_complex_scan_group reporting mismatched free block counts. With DOUBLE_CHECK macro enabled, false corruption reports may appear. No data corruption or privilege escalation is documented; the impact is limited to misleading warnings that could prompt unnecessary fsck operations.

Mitigation

Fixed by commit c05033cfc5c7699cd4df8d48cef94d01da755f24 in the Linux kernel stable tree [1]. Users should update to a kernel version containing this patch. No workaround available. Not listed on CISA KEV.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

10
29a07d691d28

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 65335248825ce4..2a6ed0b2785519 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1712,16 +1712,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1763,7 +1764,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     	poff = block % blocks_per_page;
     
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
f29709a7a3fc

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 56d50fd3310b47..de4cacb740b33e 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1706,16 +1706,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1764,7 +1765,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* we need another folio for the buddy */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
bdc56a9c46b2

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 56d50fd3310b47..de4cacb740b33e 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1706,16 +1706,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1764,7 +1765,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* we need another folio for the buddy */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
57e83bfbe1e4

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 877b336c651f79..d0f4e5905bf12d 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1634,16 +1634,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1685,7 +1686,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     	poff = block % blocks_per_page;
     
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
c05033cfc5c7

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index ed2d63b2e91dc1..09b45729398620 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1638,16 +1638,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1689,7 +1690,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     	poff = block % blocks_per_page;
     
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
29a07d691d28

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 65335248825ce4..2a6ed0b2785519 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1712,16 +1712,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1763,7 +1764,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     	poff = block % blocks_per_page;
     
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
c05033cfc5c7

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index ed2d63b2e91dc1..09b45729398620 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1638,16 +1638,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1689,7 +1690,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     	poff = block % blocks_per_page;
     
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
f29709a7a3fc

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 56d50fd3310b47..de4cacb740b33e 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1706,16 +1706,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1764,7 +1765,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* we need another folio for the buddy */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
57e83bfbe1e4

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 877b336c651f79..d0f4e5905bf12d 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1634,16 +1634,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1685,7 +1686,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     	poff = block % blocks_per_page;
     
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    
bdc56a9c46b2

ext4: fix e4b bitmap inconsistency reports

1 file changed · +11 11
  • fs/ext4/mballoc.c+11 11 modified
    diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
    index 56d50fd3310b47..de4cacb740b33e 100644
    --- a/fs/ext4/mballoc.c
    +++ b/fs/ext4/mballoc.c
    @@ -1706,16 +1706,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* Avoid locking the folio in the fast path ... */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
    +		/*
    +		 * folio_test_locked is employed to detect ongoing folio
    +		 * migrations, since concurrent migrations can lead to
    +		 * bitmap inconsistency. And if we are not uptodate that
    +		 * implies somebody just created the folio but is yet to
    +		 * initialize it. We can drop the folio reference and
    +		 * try to get the folio with lock in both cases to avoid
    +		 * concurrency.
    +		 */
     		if (!IS_ERR(folio))
    -			/*
    -			 * drop the folio reference and try
    -			 * to get the folio with lock. If we
    -			 * are not uptodate that implies
    -			 * somebody just created the folio but
    -			 * is yet to initialize it. So
    -			 * wait for it to initialize.
    -			 */
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
     				FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
    @@ -1764,7 +1765,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
     
     	/* we need another folio for the buddy */
     	folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
    -	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
    +	if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) {
     		if (!IS_ERR(folio))
     			folio_put(folio);
     		folio = __filemap_get_folio(inode->i_mapping, pnum,
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Race condition: the fast path of ext4_mb_load_buddy only increments the folio's reference count, which is insufficient to prevent concurrent folio migration from corrupting the bitmap."

Attack vector

An attacker who can trigger mixed huge-page workloads and stress I/O can exploit a race between folio migration and ext4 bitmap modification [patch_id=2661134]. During migration, `__migrate_folio` holds the folio lock while copying data; concurrently, `ext4_mb_load_buddy`'s fast path obtains the folio via `folio_try_get` (no lock) and proceeds to modify the bitmap via `mb_mark_used`. The migration then freezes the folio reference and completes the copy, causing the bitmap modification to be lost or corrupted [patch_id=2661134]. This leads to e4b bitmap inconsistency errors such as "group 350, 8179 free clusters as per group info. But got 8192 blocks" [patch_id=2661134].

Affected code

The vulnerability is in the `ext4_mb_load_buddy_gfp` function in `fs/ext4/mballoc.c` [patch_id=2661134]. The fast path uses `__filemap_get_folio` with `FGP_ACCESSED` (no lock), which only increments the folio's reference count — insufficient to prevent concurrent folio migration [patch_id=2661134].

What the fix does

The patch adds a `folio_test_locked(folio)` check to the fast-path condition in both bitmap-folio lookups within `ext4_mb_load_buddy_gfp` [patch_id=2661134]. If the folio is locked (indicating an ongoing migration), the fast path is abandoned and the slow path is taken, which acquires the folio lock via `FGP_LOCK` [patch_id=2661134]. This closes the race window because the slow path serializes with the migration's `folio_lock`, preventing bitmap modifications from being lost during `folio_mc_copy` [patch_id=2661134]. The same check also fixes a false-positive DOUBLE_CHECK race where one thread initializes the bitmap while another thread concurrently reads and modifies it without holding the lock [patch_id=2661134].

Preconditions

  • inputThe system must be running mixed huge-page workloads that trigger folio migration concurrently with ext4 block allocation
  • inputThe attacker must be able to generate sufficient I/O stress to hit the narrow race window between migration and bitmap modification

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.