A vulnerability has been discovered and resolved in the Linux kernel's handling of HFS+ (Hierarchical File System Plus), a file system developed by Apple Inc. for macOS. The issue is related to a corruption that can occur during the shrinking truncate operation.

The vulnerability was introduced by commit 31651c607151 ("hfsplus: avoid deadlock on file truncation") and has been assigned CVE-2021-46989. This post provides an overview of the vulnerability, a code snippet demonstrating the issue, and information on how to reproduce and resolve the issue.

Vulnerability Details

HFS+ uses extent records, which always contains eight extents. When the first extent record in the catalog file is full, new ones are allocated from the extents overflow file. If a shrinking truncate operation occurs in the middle of an extent record located in the extents overflow file, the logic in hfsplus_file_truncate() is changed so that the call to hfs_brec_remove() is not guarded anymore.

The correct action would be to free the extents that exceed the new size within the extent record by calling hfsplus_free_extents() and then check if the entire extent record should be removed. However, due to the misplaced guard (blk_cnt > start), the last matching extent record is removed unconditionally, causing corruption and data loss.

Create a file with at least 10 extents.

2. Perform a shrinking truncate operation in the middle of the last extent record, ensuring that the number of remaining extents is not under or divisible by eight.

The last extent record (eight extents) will be removed entirely instead of being truncated, causing corruption and data loss.

To fix this issue, check if the new truncated end is below the start of the extent record, making it safe to remove the entire extent record. Additionally, the call to hfs_brec_remove() cannot be moved to its previous place since it would result in the dropping of the ->tree_lock, causing a race condition and potentially corrupting the node data.

Another related issue is the lack of ->tree_lock. When entering the block (blk_cnt > start), the lock is not held, causing a potential race condition with hfs_find_exit() unlocking it. To address this, take the lock just before the check, maintaining proper locking balance.

The following code snippet demonstrates the fix for the vulnerability

    // ...
    if (blk_cnt > start) {
        // Take the lock before checking
        hfsplus_tree_lock(inode->i_mapping->host);
        if (end < start) {
            // Passed the check, safe to remove the entire extent record
            kaddr = hfs_bnode_write_lock(bnode);
            hfs_brec_remove(&fd);
            hfs_bnode_write_unlock(bnode);
        } else {
            hfsplus_free_extents(inode, extents, blk_cnt, end - start);
    // ...

Original References

- Linux Kernel Commit: Link
- Patch submission: Link
- Debian Security Tracker: Link

Conclusion

CVE-2021-46989 is a vulnerability in the Linux kernel's HFS+ file system that could cause corruption and data loss during shrinking truncate operations. By following the aforementioned steps to reproduce and fix the issue, you can ensure the safe removal of extent records, avoiding data corruption and potential data loss.

Timeline

Published on: 02/28/2024 09:15:37 UTC
Last modified on: 02/28/2024 14:06:45 UTC