]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: handle -EAGAIN from btrfs_duplicate_item and refresh stale leaf pointer
authorrobbieko <robbieko@synology.com>
Mon, 13 Apr 2026 06:52:36 +0000 (14:52 +0800)
committerDavid Sterba <dsterba@suse.com>
Tue, 21 Apr 2026 02:02:34 +0000 (04:02 +0200)
In the 'punch a hole' case of btrfs_delete_raid_extent(),
btrfs_duplicate_item() can return -EAGAIN when the leaf needs to be
split and the path becomes invalid. The old code treats any error as
fatal and breaks out of the loop.

Additionally, btrfs_duplicate_item() may trigger setup_leaf_for_split()
which can reallocate the leaf node. The code continues using the old
leaf pointer, leading to use-after-free or stale data access.

Fix both issues by:

- Handling -EAGAIN specifically: release the path and retry the loop.
- Refreshing leaf = path->nodes[0] after successful duplication.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: robbieko <robbieko@synology.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/raid-stripe-tree.c

index d454894b9e665a996181e3681691665aba9eac81..2e0d2f83c6516f38cd1fd4ac1cc18e0c26fc07ce 100644 (file)
@@ -194,9 +194,19 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
 
                        /* The "right" item. */
                        ret = btrfs_duplicate_item(trans, stripe_root, path, &newkey);
+                       if (ret == -EAGAIN) {
+                               btrfs_release_path(path);
+                               continue;
+                       }
                        if (ret)
                                break;
 
+                       /*
+                        * btrfs_duplicate_item() may have triggered a leaf
+                        * split via setup_leaf_for_split(), so we must refresh
+                        * our leaf pointer from the path.
+                        */
+                       leaf = path->nodes[0];
                        item_size = btrfs_item_size(leaf, path->slots[0]);
                        extent = btrfs_item_ptr(leaf, path->slots[0],
                                                struct btrfs_stripe_extent);