[PATCH] btrfs: fix use-after-free of reloc_control in btrfs_reloc_cow_block
From: Yun Zhou
Date: Sun Jun 07 2026 - 01:35:00 EST
btrfs_reloc_cow_block() reads fs_info->reloc_ctl without holding any
lock and then uses the obtained rc pointer throughout the function
(including replace_file_extents which may sleep). Meanwhile, the
balance error path can concurrently call unset_reloc_control() followed
by free_reloc_control(), freeing rc while btrfs_reloc_cow_block() is
still using it.
The race window exists because btrfs_commit_current_transaction() in
relocate_block_group() may return immediately when the filesystem is in
error state (no running transaction to wait for), allowing
free_reloc_control() to execute while other threads still hold
references to rc obtained before unset_reloc_control().
Fix this by adding a btrfs_commit_current_transaction() call in
btrfs_relocate_block_group() right before free_reloc_control(). This
ensures that any concurrent thread that obtained rc via reloc_ctl
(which requires being in a transaction context) has completed its
transaction before rc is freed.
Reported-by: syzbot+0eea49bba18051dea35e@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=0eea49bba18051dea35e
Signed-off-by: Yun Zhou <yun.zhou@xxxxxxxxxxxxx>
---
fs/btrfs/relocation.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 3ebaf5880125..a01262d0b2ab 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -5443,6 +5443,14 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
reloc_chunk_end(fs_info);
out_put_bg:
btrfs_put_block_group(bg);
+ /*
+ * Ensure no concurrent btrfs_reloc_cow_block() is still using rc.
+ * After unset_reloc_control() new callers will see reloc_ctl == NULL
+ * and return immediately. But callers that read reloc_ctl before unset
+ * are still in a transaction. Wait for the current transaction to
+ * complete so all such callers have finished using rc.
+ */
+ btrfs_commit_current_transaction(fs_info->tree_root);
free_reloc_control(rc);
return ret;
}
--
2.43.0