All zones of a zoned null_blk device are currently initialized with the same size. However, if the specified size (capacity) of the null_blk device is not a multiple of the zone size, the last zone becomes too large and read/write accesses to it can cause out of bound errors. Fix this by correctly setting the size of the last zone of the device to the remainder of the disk capacity if this capacity is not a multiple of the zone size. For such smaller last zone, the zone capacity is also checked so that it does not exceed the smaller zone size. Reported-by: Naohiro Aota <naohiro.aota@xxxxxxx> Signed-off-by: Damien Le Moal <damien.lemoal@xxxxxxx> --- drivers/block/null_blk_zoned.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/block/null_blk_zoned.c b/drivers/block/null_blk_zoned.c index beb34b4f76b0..18911e67f792 100644 --- a/drivers/block/null_blk_zoned.c +++ b/drivers/block/null_blk_zoned.c @@ -16,7 +16,7 @@ static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect) int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q) { - sector_t dev_size = (sector_t)dev->size * 1024 * 1024; + sector_t dev_capacity, zone_capacity; sector_t sector = 0; unsigned int i; @@ -38,9 +38,14 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q) return -EINVAL; } + zone_capacity = dev->zone_capacity << ZONE_SIZE_SHIFT; + dev_capacity = ((sector_t)dev->size * SZ_1M) >> SECTOR_SHIFT; + dev->zone_size_sects = dev->zone_size << ZONE_SIZE_SHIFT; - dev->nr_zones = dev_size >> - (SECTOR_SHIFT + ilog2(dev->zone_size_sects)); + dev->nr_zones = dev_capacity >> ilog2(dev->zone_size_sects); + if (dev_capacity & (dev->zone_size_sects - 1)) + dev->nr_zones++; + dev->zones = kvmalloc_array(dev->nr_zones, sizeof(struct blk_zone), GFP_KERNEL | __GFP_ZERO); if (!dev->zones) @@ -101,8 +106,12 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q) struct blk_zone *zone = &dev->zones[i]; zone->start = zone->wp = sector; - zone->len = dev->zone_size_sects; - zone->capacity = dev->zone_capacity << ZONE_SIZE_SHIFT; + if (zone->start + dev->zone_size_sects > dev_capacity) + zone->len = dev_capacity - zone->start; + else + zone->len = dev->zone_size_sects; + zone->capacity = + min_t(sector_t, zone->len, zone_capacity); zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ; zone->cond = BLK_ZONE_COND_EMPTY; -- 2.26.2