summaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
authorZHOU Jiaxiang <me@fxti.xyz>2026-09-16 21:58:21 +0800
committerMartin K. Petersen (Oracle) <mkp@kernel.org>2026-09-16 10:11:45 -0400
commit7c431d61b69a3fd0784c20aa4cd0b8fb501b5653 (patch)
tree03ea9189ea25b8c6dcc470adf6a6cface1036195 /block
parent278210c60c6f6958bd2eeaa2120c862683b83d09 (diff)
downloadlwn-7c431d61b69a3fd0784c20aa4cd0b8fb501b5653.tar.gz
lwn-7c431d61b69a3fd0784c20aa4cd0b8fb501b5653.zip
scsi: block: Fix zones_cond out-of-bounds write on zone report
blk_revalidate_disk_zones() sizes the zones_cond array from the disk capacity and zone size, but the index used by blk_revalidate_zone_cond() comes from the device-driven report_zones() walk and is never checked against the array size. A device reporting more zones than fit the array makes blk_zone_set_cond() write out of bounds. One way to reach this is a zone count exceeding 32 bits: both blk_revalidate_zone_args.nr_zones and struct zoned_disk_info.nr_zones are unsigned int, so a disk advertising more than UINT_MAX zones (e.g. 2^32 + 1024 zones of one 512-byte logical block) gets its zone count truncated to a small value, undersizing the array while the report walk keeps counting upward. Check the index against the array size before storing the zone condition, and refuse to revalidate when the zone count does not fit 32 bits. Fixes: 6e945ffb6555 ("block: use zone condition to determine conventional zones") Signed-off-by: ZHOU Jiaxiang <me@fxti.xyz> Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Link: https://patch.msgid.link/7815D1B293A8F55E+20260916135822.32584-2-me@fxti.xyz Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
Diffstat (limited to 'block')
-rw-r--r--block/blk-zoned.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index a5afb842bf35..475aa16bc41a 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -2018,12 +2018,17 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
struct blk_revalidate_zone_args *args)
{
struct queue_limits *lim = &disk->queue->limits;
+ unsigned long long nr_zones;
unsigned int pool_size;
int ret = 0;
args->disk = disk;
- args->nr_zones =
- DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
+ nr_zones = DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
+ if (nr_zones > UINT_MAX) {
+ pr_warn("%s: Too many zones (%llu)\n", disk->disk_name, nr_zones);
+ return -EINVAL;
+ }
+ args->nr_zones = nr_zones;
/* Cached zone conditions: 1 byte per zone */
args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO);
@@ -2131,6 +2136,12 @@ static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
{
enum blk_zone_cond cond = zone->cond;
+ if (idx >= args->nr_zones) {
+ pr_warn("%s: Zone report index %u exceeds zone count %u\n",
+ args->disk->disk_name, idx, args->nr_zones);
+ return -EINVAL;
+ }
+
/* Check that the zone condition is consistent with the zone type. */
switch (cond) {
case BLK_ZONE_COND_NOT_WP: