[BUG] Five data races in btrfs
Hello maintainers,
I would like to report five data race bugs we discovered in the BTRFS
filesystem on Linux kernel v6.16-rc3. These issues were identified
using our data race detector.
We are currently analyzing the Btrfs codebase and have identified
several instances that may involve potential data races during
concurrent execution. While we have reviewed the relevant code
paths carefully, we are uncertain whether these issues could lead
to any practical impact or system instability.
Below is a summary of the findings:
---
1. Race between `__btrfs_set_fs_incompat()` and `btrfs_fs_incompat()`
`__btrfs_set_fs_incompat()` performs a write to the
`super_copy->incompat_flags` field under `fs_info->super_lock` while
`btrfs_need_stripe_tree_update()` calls `btrfs_fs_incompat()` without
acquiring `super_lock`, which may read a stale or partially updated
value of `incompat_flags`.
---I do not think we should hold any inode lock just to access defrag_compress.
2. Race between `btrfs_defrag_file()` and `inode_need_compress()`
In `btrfs_defrag_file()`, the field `inode->defrag_compress` is
assigned while holding the inode lock via `btrfs_inode_lock()`.
But in `inode_need_compress()`, the same field is read without
any apparent locking or memory barrier.
---
3. Race between `join_transaction()` and `btrfs_get_fs_generation()`
In `join_transaction()`, `fs_info->generation` is assigned while
holding the lock `fs_info->trans_lock`. But reads of
`fs_info->generation` are done using READ_ONCE() in
`btrfs_get_fs_generation()`.
---
4. Race between `btrfs_super_bytes_used()` and `btrfs_update_block_group()`
In `btrfs_set_backup_bytes_used()`, super_copy is read and stored without
holding lock `info->delalloc_root_lock`. But in `btrfs_update_block_group()`
the `info->super_copy` is set concurrently.
---
5. Race between `btrfs_defrag_file()` and `btrfs_defrag_file()`
In the function btrfs_defrag_file(), we also noticed a possible
race condition involving the writeback_index field of the
address_space structure associated with the inode. Specifically,
the code performs a read and conditional write without any
evident locking: