Skip to content

Commit 21e61ec

Browse files
morbidrsakdave
authored andcommitted
btrfs: zoned: clone zoned device info when cloning a device
When cloning a btrfs_device, we're not cloning the associated btrfs_zoned_device_info structure of the device in case of a zoned filesystem. Later on this leads to a NULL pointer dereference when accessing the device's zone_info for instance when setting a zone as active. This was uncovered by fstests' testcase btrfs/161. CC: [email protected] # 5.15+ Signed-off-by: Johannes Thumshirn <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent b75b51f commit 21e61ec

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

fs/btrfs/volumes.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,18 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
10111011
rcu_assign_pointer(device->name, name);
10121012
}
10131013

1014+
if (orig_dev->zone_info) {
1015+
struct btrfs_zoned_device_info *zone_info;
1016+
1017+
zone_info = btrfs_clone_dev_zone_info(orig_dev);
1018+
if (!zone_info) {
1019+
btrfs_free_device(device);
1020+
ret = -ENOMEM;
1021+
goto error;
1022+
}
1023+
device->zone_info = zone_info;
1024+
}
1025+
10141026
list_add(&device->dev_list, &fs_devices->devices);
10151027
device->fs_devices = fs_devices;
10161028
fs_devices->num_devices++;

fs/btrfs/zoned.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,46 @@ void btrfs_destroy_dev_zone_info(struct btrfs_device *device)
639639
device->zone_info = NULL;
640640
}
641641

642+
struct btrfs_zoned_device_info *btrfs_clone_dev_zone_info(struct btrfs_device *orig_dev)
643+
{
644+
struct btrfs_zoned_device_info *zone_info;
645+
646+
zone_info = kmemdup(orig_dev->zone_info, sizeof(*zone_info), GFP_KERNEL);
647+
if (!zone_info)
648+
return NULL;
649+
650+
zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
651+
if (!zone_info->seq_zones)
652+
goto out;
653+
654+
bitmap_copy(zone_info->seq_zones, orig_dev->zone_info->seq_zones,
655+
zone_info->nr_zones);
656+
657+
zone_info->empty_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
658+
if (!zone_info->empty_zones)
659+
goto out;
660+
661+
bitmap_copy(zone_info->empty_zones, orig_dev->zone_info->empty_zones,
662+
zone_info->nr_zones);
663+
664+
zone_info->active_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
665+
if (!zone_info->active_zones)
666+
goto out;
667+
668+
bitmap_copy(zone_info->active_zones, orig_dev->zone_info->active_zones,
669+
zone_info->nr_zones);
670+
zone_info->zone_cache = NULL;
671+
672+
return zone_info;
673+
674+
out:
675+
bitmap_free(zone_info->seq_zones);
676+
bitmap_free(zone_info->empty_zones);
677+
bitmap_free(zone_info->active_zones);
678+
kfree(zone_info);
679+
return NULL;
680+
}
681+
642682
int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
643683
struct blk_zone *zone)
644684
{

fs/btrfs/zoned.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
3636
int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info);
3737
int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache);
3838
void btrfs_destroy_dev_zone_info(struct btrfs_device *device);
39+
struct btrfs_zoned_device_info *btrfs_clone_dev_zone_info(struct btrfs_device *orig_dev);
3940
int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info);
4041
int btrfs_check_mountopts_zoned(struct btrfs_fs_info *info);
4142
int btrfs_sb_log_location_bdev(struct block_device *bdev, int mirror, int rw,
@@ -103,6 +104,16 @@ static inline int btrfs_get_dev_zone_info(struct btrfs_device *device,
103104

104105
static inline void btrfs_destroy_dev_zone_info(struct btrfs_device *device) { }
105106

107+
/*
108+
* In case the kernel is compiled without CONFIG_BLK_DEV_ZONED we'll never call
109+
* into btrfs_clone_dev_zone_info() so it's safe to return NULL here.
110+
*/
111+
static inline struct btrfs_zoned_device_info *btrfs_clone_dev_zone_info(
112+
struct btrfs_device *orig_dev)
113+
{
114+
return NULL;
115+
}
116+
106117
static inline int btrfs_check_zoned_mode(const struct btrfs_fs_info *fs_info)
107118
{
108119
if (!btrfs_is_zoned(fs_info))

0 commit comments

Comments
 (0)