Skip to content

Commit 0090d6e

Browse files
fdmananakdave
authored andcommitted
btrfs: zoned: fix use-after-free due to race with dev replace
While loading a zone's info during creation of a block group, we can race with a device replace operation and then trigger a use-after-free on the device that was just replaced (source device of the replace operation). This happens because at btrfs_load_zone_info() we extract a device from the chunk map into a local variable and then use the device while not under the protection of the device replace rwsem. So if there's a device replace operation happening when we extract the device and that device is the source of the replace operation, we will trigger a use-after-free if before we finish using the device the replace operation finishes and frees the device. Fix this by enlarging the critical section under the protection of the device replace rwsem so that all uses of the device are done inside the critical section. CC: stable@vger.kernel.org # 6.1.x: 15c12fc: btrfs: zoned: introduce a zone_info struct in btrfs_load_block_group_zone_info CC: stable@vger.kernel.org # 6.1.x: 09a4672: btrfs: zoned: factor out per-zone logic from btrfs_load_block_group_zone_info CC: stable@vger.kernel.org # 6.1.x: 9e0e3e7: btrfs: zoned: factor out single bg handling from btrfs_load_block_group_zone_info CC: stable@vger.kernel.org # 6.1.x: 87463f7: btrfs: zoned: factor out DUP bg handling from btrfs_load_block_group_zone_info CC: stable@vger.kernel.org # 6.1.x Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 2b8aa78 commit 0090d6e

File tree

1 file changed

+10
-3
lines changed

1 file changed

+10
-3
lines changed

fs/btrfs/zoned.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,15 +1290,19 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
12901290
struct btrfs_chunk_map *map)
12911291
{
12921292
struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
1293-
struct btrfs_device *device = map->stripes[zone_idx].dev;
1293+
struct btrfs_device *device;
12941294
int dev_replace_is_ongoing = 0;
12951295
unsigned int nofs_flag;
12961296
struct blk_zone zone;
12971297
int ret;
12981298

12991299
info->physical = map->stripes[zone_idx].physical;
13001300

1301+
down_read(&dev_replace->rwsem);
1302+
device = map->stripes[zone_idx].dev;
1303+
13011304
if (!device->bdev) {
1305+
up_read(&dev_replace->rwsem);
13021306
info->alloc_offset = WP_MISSING_DEV;
13031307
return 0;
13041308
}
@@ -1308,18 +1312,17 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
13081312
__set_bit(zone_idx, active);
13091313

13101314
if (!btrfs_dev_is_sequential(device, info->physical)) {
1315+
up_read(&dev_replace->rwsem);
13111316
info->alloc_offset = WP_CONVENTIONAL;
13121317
return 0;
13131318
}
13141319

13151320
/* This zone will be used for allocation, so mark this zone non-empty. */
13161321
btrfs_dev_clear_zone_empty(device, info->physical);
13171322

1318-
down_read(&dev_replace->rwsem);
13191323
dev_replace_is_ongoing = btrfs_dev_replace_is_ongoing(dev_replace);
13201324
if (dev_replace_is_ongoing && dev_replace->tgtdev != NULL)
13211325
btrfs_dev_clear_zone_empty(dev_replace->tgtdev, info->physical);
1322-
up_read(&dev_replace->rwsem);
13231326

13241327
/*
13251328
* The group is mapped to a sequential zone. Get the zone write pointer
@@ -1330,6 +1333,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
13301333
ret = btrfs_get_dev_zone(device, info->physical, &zone);
13311334
memalloc_nofs_restore(nofs_flag);
13321335
if (ret) {
1336+
up_read(&dev_replace->rwsem);
13331337
if (ret != -EIO && ret != -EOPNOTSUPP)
13341338
return ret;
13351339
info->alloc_offset = WP_MISSING_DEV;
@@ -1341,6 +1345,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
13411345
"zoned: unexpected conventional zone %llu on device %s (devid %llu)",
13421346
zone.start << SECTOR_SHIFT, rcu_str_deref(device->name),
13431347
device->devid);
1348+
up_read(&dev_replace->rwsem);
13441349
return -EIO;
13451350
}
13461351

@@ -1368,6 +1373,8 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
13681373
break;
13691374
}
13701375

1376+
up_read(&dev_replace->rwsem);
1377+
13711378
return 0;
13721379
}
13731380

0 commit comments

Comments
 (0)