A major vulnerability (CVE-2025-21667) was found in the Linux kernel, which could lead to an infinite loop when writing to an XFS filesystem on 32-bit kernels. This bug occurs in the iomap_write_delalloc_scan() function, and is caused by the truncation of a 64-bit offset to 32 bits. The problem has been resolved by avoiding this truncation and making sure that the correct data type is used for the file position.

In this post, we will discuss the details of this vulnerability, including the problematic code and the fix that has been applied. We will also provide links to the original sources, including the Linux Kernel Mailing List (LKML) discussion and the patch submission.

Vulnerability Details

The vulnerability is specific to Linux kernels running on 32-bit systems, particularly those using the XFS filesystem. The issue arises in the iomap_write_delalloc_scan() function, which scans a range of pages for delalloc structures.

The problem begins with the folio_next_index() function, which is supposed to return an unsigned long for the file position. However, due to an oversight in the code, it was instead returning a 32-bit position. This could lead to the lower 32 bits of the position wrapping around when the actual position exceeds 2^32.

This can cause an infinite loop in the iomap_write_delalloc_scan() function when it tries to write data to the filesystem, causing the system to become unresponsive and potentially resulting in data loss or corruption.

Here's the problematic code snipplet

    for (pos = folio_next_index(folio);
         end != pos && block_in_page != next_offset &&
         (u64)new_folio_start(folio) >> inode->i_blkbits ==
         (u64)folio->index << PAGE_SHIFT >> inode->i_blkbits &&
         can_extend_folio(folio, prev_folio);
         pos = folio_next_index(folio)) {

In this loop, pos is defined as an unsigned long, but folio_next_index() returns a 32-bit value. Consequently, the loop keeps iterating even when the correct stopping condition has been reached, leading to the infinite loop.

Resolution

A patch was submitted to the Linux kernel, and the vulnerability has been resolved. The fix was to specifically cast the return value of folio_next_index() to u64, to ensure that a 64-bit value is used for the file position, avoiding the truncation issue.

Here's the patched code snipplet

    for (pos = (u64)folio_next_index(folio);
         end != pos && block_in_page != next_offset &&
         (u64)new_folio_start(folio) >> inode->i_blkbits ==
         (u64)folio->index << PAGE_SHIFT >> inode->i_blkbits &&
         can_extend_folio(folio, prev_folio);
         pos = (u64)folio_next_index(folio)) {

With this change, the function now behaves as expected, preventing the infinite loop and ensuring correct data handling.

References and Additional Information

You can find the full discussion about this vulnerability and its resolution on the Linux Kernel Mailing List (LKML) here. The patch itself can be found here.

Please make sure your Linux kernel is up-to-date, and check with your distribution or vendor for the availability of a patched kernel version that resolves CVE-2025-21667.

Timeline

Published on: 01/31/2025 12:15:27 UTC
Last modified on: 02/03/2025 20:00:28 UTC