mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [BUG] Five data races in btrfs
@ 2025-06-26  8:23 haoran zheng
  2025-06-26  9:10 ` Qu Wenruo
  0 siblings, 1 reply; 2+ messages in thread
From: haoran zheng @ 2025-06-26  8:23 UTC (permalink / raw)
  To: clm, josef, dsterba; +Cc: linux-btrfs, linux-kernel, baijiaju1990, zzzccc427

[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`.

===========================DATARACE===========================
 __btrfs_set_fs_incompat+0x12b/0x150 fs/btrfs/fs.c:150
 btrfs_ioctl_default_subvol+0x2e5/0x380 fs/btrfs/ioctl.c:2930
 btrfs_ioctl+0xd1f/0xe40 fs/btrfs/ioctl.c:5249
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:906 [inline]
 __se_sys_ioctl+0x91/0xf0 fs/ioctl.c:892
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 0x0
============OTHER_INFO============
 get_unaligned_le64 include/linux/unaligned.h:28 [inline]
 btrfs_super_incompat_flags fs/btrfs/accessors.h:890 [inline]
 btrfs_need_stripe_tree_update fs/btrfs/raid-stripe-tree.h:42 [inline]
 btrfs_map_block+0x426/0x1960 fs/btrfs/volumes.c:6674
 btrfs_submit_chunk fs/btrfs/bio.c:694 [inline]
 btrfs_submit_bbio+0x291/0xa70 fs/btrfs/bio.c:799
 write_one_eb+0x8d8/0x960 fs/btrfs/extent_io.c:1828
 submit_eb_page fs/btrfs/extent_io.c:1985 [inline]
 btree_write_cache_pages+0x3b1/0x8e0 fs/btrfs/extent_io.c:2035
 btree_writepages+0x6b/0x190 fs/btrfs/disk-io.c:520
 do_writepages+0x102/0x370 mm/page-writeback.c:2687
 filemap_fdatawrite_wbc mm/filemap.c:389 [inline]
 __filemap_fdatawrite_range mm/filemap.c:422 [inline]
 filemap_fdatawrite_range+0x9a/0xd0 mm/filemap.c:440
 btrfs_write_marked_extents+0x130/0x230 fs/btrfs/transaction.c:1150
 btrfs_sync_log+0xcfd/0x11f0 fs/btrfs/tree-log.c:3113
 btrfs_sync_file+0x74b/0xaa0 fs/btrfs/file.c:1692
 generic_write_sync include/linux/fs.h:2970 [inline]
 btrfs_do_write_iter+0x4ba/0x5a0 fs/btrfs/file.c:1391
 btrfs_file_write_iter+0x3d/0x60 fs/btrfs/file.c:1401
 new_sync_write fs/read_write.c:586 [inline]
 vfs_write+0x379/0x580 fs/read_write.c:679
 ksys_write+0x93/0x120 fs/read_write.c:731
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
=================================

---

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.

===========================DATARACE===========================
 btrfs_defrag_file+0x127d/0x1570 fs/btrfs/defrag.c:1501
 btrfs_ioctl_defrag+0x256/0x2f0 fs/btrfs/ioctl.c:2574
 btrfs_ioctl+0xba4/0xe40
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:906 [inline]
 __se_sys_ioctl+0x91/0xf0 fs/ioctl.c:892
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 0x0
============OTHER_INFO============
 inode_need_compress fs/btrfs/inode.c:788 [inline]
 btrfs_run_delalloc_range+0x206/0x8a0 fs/btrfs/inode.c:2336
 writepage_delalloc+0x52c/0x8b0 fs/btrfs/extent_io.c:1255
 extent_writepage fs/btrfs/extent_io.c:1558 [inline]
 extent_write_cache_pages fs/btrfs/extent_io.c:2248 [inline]
 btrfs_writepages+0x930/0xe70 fs/btrfs/extent_io.c:2376
 do_writepages+0x102/0x370 mm/page-writeback.c:2687
 filemap_fdatawrite_wbc mm/filemap.c:389 [inline]
 __filemap_fdatawrite_range mm/filemap.c:422 [inline]
 filemap_fdatawrite_range+0x9a/0xd0 mm/filemap.c:440
 btrfs_fdatawrite_range+0x6b/0xf0 fs/btrfs/file.c:3701
 btrfs_direct_write+0x37d/0x6b0 fs/btrfs/direct-io.c:960
 btrfs_do_write_iter+0x21f/0x5a0 fs/btrfs/file.c:1381
 btrfs_file_write_iter+0x3d/0x60 fs/btrfs/file.c:1401
 new_sync_write fs/read_write.c:586 [inline]
 vfs_write+0x379/0x580 fs/read_write.c:679
 ksys_write+0x93/0x120 fs/read_write.c:731
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
=================================

---

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()`.

===========================DATARACE===========================
 join_transaction+0x6ee/0x740 fs/btrfs/transaction.c:390
 start_transaction+0x54e/0xea0 fs/btrfs/transaction.c:699
 btrfs_join_transaction+0x42/0x60 fs/btrfs/transaction.c:823
 btrfs_dirty_inode+0xa6/0x1c0 fs/btrfs/inode.c:6093
 btrfs_update_time+0xa3/0xe0 fs/btrfs/inode.c:6127
 inode_update_time fs/inode.c:2124 [inline]
 touch_atime+0xb7/0x1d0 fs/inode.c:2197
 file_accessed include/linux/fs.h:2601 [inline]
 filemap_read+0x49d/0x520 mm/filemap.c:2763
 btrfs_file_read_iter+0x71/0x190 fs/btrfs/file.c:3658
 new_sync_read fs/read_write.c:484 [inline]
 vfs_read+0x311/0x460 fs/read_write.c:565
 ksys_read+0x93/0x120 fs/read_write.c:708
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 0x0
============OTHER_INFO============
 btrfs_get_fs_generation fs/btrfs/fs.h:915 [inline]
 try_release_extent_mapping+0x13b/0x3a0 fs/btrfs/extent_io.c:2489
 __btrfs_release_folio fs/btrfs/inode.c:7267 [inline]
 btrfs_invalidate_folio+0x666/0x730 fs/btrfs/inode.c:7466
 folio_invalidate mm/truncate.c:126 [inline]
 truncate_cleanup_folio+0xf5/0x130 mm/truncate.c:146
 truncate_inode_pages_range+0x126/0x4c0 mm/truncate.c:326
 truncate_inode_pages mm/truncate.c:407 [inline]
 truncate_pagecache mm/truncate.c:716 [inline]
 truncate_setsize+0x71/0x90 mm/truncate.c:741
 btrfs_setsize fs/btrfs/inode.c:5126 [inline]
 btrfs_setattr+0x351/0xa30 fs/btrfs/inode.c:5165
 notify_change+0x4d8/0x550 fs/attr.c:552
 do_truncate+0x11b/0x160 fs/open.c:65
 handle_truncate fs/namei.c:3451 [inline]
 do_open fs/namei.c:3834 [inline]
 path_openat+0xfad/0x1210 fs/namei.c:3989
 do_filp_open+0xda/0x1d0 fs/namei.c:4016
 do_sys_openat2+0x91/0x100 fs/open.c:1428
 do_sys_open fs/open.c:1443 [inline]
 __do_sys_open fs/open.c:1451 [inline]
 __se_sys_open fs/open.c:1447 [inline]
 __x64_sys_open+0xac/0xd0 fs/open.c:1447
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
=================================

---

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.

===========================DATARACE===========================
 get_unaligned_le64 include/linux/unaligned.h:28 [inline]
 btrfs_super_bytes_used fs/btrfs/accessors.h:874 [inline]
 backup_super_roots fs/btrfs/disk-io.c:1706 [inline]
 write_all_supers+0xf9d/0x1dc0 fs/btrfs/disk-io.c:4101
 btrfs_commit_transaction+0xf73/0x1c40 fs/btrfs/transaction.c:2528
 transaction_kthread+0x1f8/0x330 fs/btrfs/disk-io.c:1602
 kthread+0x2d5/0x300 kernel/kthread.c:464
 ret_from_fork+0x4d/0x60 arch/x86/kernel/process.c:148
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
============OTHER_INFO============
 btrfs_update_block_group+0xf0/0x7a0 fs/btrfs/block-group.c:3650
 do_free_extent_accounting fs/btrfs/extent-tree.c:2975 [inline]
 __btrfs_free_extent+0xde5/0x1d00 fs/btrfs/extent-tree.c:3338
 btrfs_run_delayed_refs_for_head fs/btrfs/extent-tree.c:1976 [inline]
 __btrfs_run_delayed_refs+0x5a3/0x1a50 fs/btrfs/extent-tree.c:2046
 btrfs_run_delayed_refs+0xd1/0x2b0 fs/btrfs/extent-tree.c:2158
 btrfs_commit_transaction+0x27a/0x1c40 fs/btrfs/transaction.c:2196
 insert_balance_item fs/btrfs/volumes.c:3771 [inline]
 btrfs_balance+0x11a4/0x1770 fs/btrfs/volumes.c:4647
 btrfs_ioctl_balance+0x290/0x470 fs/btrfs/ioctl.c:3587
 btrfs_ioctl+0xcaf/0xe40 fs/btrfs/ioctl.c:5305
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:906 [inline]
 __se_sys_ioctl+0x91/0xf0 fs/ioctl.c:892
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77

---

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:

===========================DATARACE===========================
 btrfs_defrag_file+0x2ca/0x1570 fs/btrfs/defrag.c:1426
 btrfs_ioctl_defrag+0x256/0x2f0 fs/btrfs/ioctl.c:2574
 btrfs_ioctl+0xd56/0xe40
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:906 [inline]
 __se_sys_ioctl+0x91/0xf0 fs/ioctl.c:892
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 0x0
============OTHER_INFO============
 btrfs_defrag_file+0x2fa/0x1570 fs/btrfs/defrag.c:1427
 btrfs_ioctl_defrag+0x256/0x2f0 fs/btrfs/ioctl.c:2574
 btrfs_ioctl+0xd56/0xe40
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:906 [inline]
 __se_sys_ioctl+0x91/0xf0 fs/ioctl.c:892
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xc9/0x1a0 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
=================================

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [BUG] Five data races in btrfs
  2025-06-26  8:23 [BUG] Five data races in btrfs haoran zheng
@ 2025-06-26  9:10 ` Qu Wenruo
  0 siblings, 0 replies; 2+ messages in thread
From: Qu Wenruo @ 2025-06-26  9:10 UTC (permalink / raw)
  To: haoran zheng, clm, josef, dsterba
  Cc: linux-btrfs, linux-kernel, baijiaju1990, zzzccc427



在 2025/6/26 17:53, haoran zheng 写道:
> [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 think we should cache the super block incompat/compat_ro flags into 
btrfs_fs_info, so that we can use bit operations.
> ---
> 
> 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.
I do not think we should hold any inode lock just to access defrag_compress.

Mind to explain why accessing that member without any lock can be 
problematic?

The only thing I can think of is some unaligned access,
but accessing a u8 alone shouldn't need special lock.

> ---
> 
> 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()`.

Again, I didn't see much problem accessing a single u64 value with or 
without holding a lock.


> ---
> 
> 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.

This is definitely false alert.

In btrfs_update_block_group() it holds a transaction handle, thus no one 
can commit the current transaction until the transaction handle is released.

Furthermore, btrfs_super_bytes_used() is accessing the superblock copy, 
but super block copy is only updated during the end of commit transaction.

So it's very safe to access the super block members with a trans handle 
hold.


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

This may need some extra digging, but so far I'm seeing other fses doing 
a similar work.

And considering all the above 4 cases, I'm not that confident if your 
reports are that helpful.

Thanks,
Qu


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-06-26  9:10 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-26  8:23 [BUG] Five data races in btrfs haoran zheng
2025-06-26  9:10 ` Qu Wenruo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®