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: stable@vger.kernel.org # 5.15+ Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
b75b51f886
commit
21e61ec6d0
@ -1011,6 +1011,18 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
|
||||
rcu_assign_pointer(device->name, name);
|
||||
}
|
||||
|
||||
if (orig_dev->zone_info) {
|
||||
struct btrfs_zoned_device_info *zone_info;
|
||||
|
||||
zone_info = btrfs_clone_dev_zone_info(orig_dev);
|
||||
if (!zone_info) {
|
||||
btrfs_free_device(device);
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
device->zone_info = zone_info;
|
||||
}
|
||||
|
||||
list_add(&device->dev_list, &fs_devices->devices);
|
||||
device->fs_devices = fs_devices;
|
||||
fs_devices->num_devices++;
|
||||
|
@ -639,6 +639,46 @@ void btrfs_destroy_dev_zone_info(struct btrfs_device *device)
|
||||
device->zone_info = NULL;
|
||||
}
|
||||
|
||||
struct btrfs_zoned_device_info *btrfs_clone_dev_zone_info(struct btrfs_device *orig_dev)
|
||||
{
|
||||
struct btrfs_zoned_device_info *zone_info;
|
||||
|
||||
zone_info = kmemdup(orig_dev->zone_info, sizeof(*zone_info), GFP_KERNEL);
|
||||
if (!zone_info)
|
||||
return NULL;
|
||||
|
||||
zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
|
||||
if (!zone_info->seq_zones)
|
||||
goto out;
|
||||
|
||||
bitmap_copy(zone_info->seq_zones, orig_dev->zone_info->seq_zones,
|
||||
zone_info->nr_zones);
|
||||
|
||||
zone_info->empty_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
|
||||
if (!zone_info->empty_zones)
|
||||
goto out;
|
||||
|
||||
bitmap_copy(zone_info->empty_zones, orig_dev->zone_info->empty_zones,
|
||||
zone_info->nr_zones);
|
||||
|
||||
zone_info->active_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
|
||||
if (!zone_info->active_zones)
|
||||
goto out;
|
||||
|
||||
bitmap_copy(zone_info->active_zones, orig_dev->zone_info->active_zones,
|
||||
zone_info->nr_zones);
|
||||
zone_info->zone_cache = NULL;
|
||||
|
||||
return zone_info;
|
||||
|
||||
out:
|
||||
bitmap_free(zone_info->seq_zones);
|
||||
bitmap_free(zone_info->empty_zones);
|
||||
bitmap_free(zone_info->active_zones);
|
||||
kfree(zone_info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
|
||||
struct blk_zone *zone)
|
||||
{
|
||||
|
@ -36,6 +36,7 @@ int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
|
||||
int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info);
|
||||
int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache);
|
||||
void btrfs_destroy_dev_zone_info(struct btrfs_device *device);
|
||||
struct btrfs_zoned_device_info *btrfs_clone_dev_zone_info(struct btrfs_device *orig_dev);
|
||||
int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info);
|
||||
int btrfs_check_mountopts_zoned(struct btrfs_fs_info *info);
|
||||
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,
|
||||
|
||||
static inline void btrfs_destroy_dev_zone_info(struct btrfs_device *device) { }
|
||||
|
||||
/*
|
||||
* In case the kernel is compiled without CONFIG_BLK_DEV_ZONED we'll never call
|
||||
* into btrfs_clone_dev_zone_info() so it's safe to return NULL here.
|
||||
*/
|
||||
static inline struct btrfs_zoned_device_info *btrfs_clone_dev_zone_info(
|
||||
struct btrfs_device *orig_dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int btrfs_check_zoned_mode(const struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
if (!btrfs_is_zoned(fs_info))
|
||||
|
Loading…
Reference in New Issue
Block a user