From 370ca0760b18c64592a258bcb057ff0a2531aabf Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Wed, 16 Sep 2026 17:51:36 +0800 Subject: [PATCH] md/raid5: reject a per-device size smaller than one chunk Both raid5_run() and raid5_resize() align the per-device size down to a whole multiple of the chunk size: mddev->dev_sectors &= ~(chunk_sectors - 1); Neither checks the result, so a size smaller than one chunk silently becomes zero. In raid5_run() that is harmless: the array size is derived from the same zero, so the disk just ends up with no capacity. In raid5_resize() it leaves the array inconsistent -- mddev->dev_sectors becomes zero, while mddev->array_sectors and the gendisk capacity keep the previous. A later raid4/raid5 -> raid0 takeover copies the zero into every rdev->sectors (raid0_takeover_raid45()), so create_strip_zones() builds a strip zone table whose zone_end is zero. Any read then passes bio_check_eod() against the stale capacity and hits the BUG() in find_zone(): kernel BUG at drivers/md/raid0.c:318! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI Call Trace: raid0_make_request md_handle_request submit_bio_noacct_nocheck block_read_full_folio read_part_sector bdev_disk_changed bdev_open Reject a size smaller than one chunk in both functions, so that a running raid4/raid5 array always has mddev->dev_sectors >= one chunk. The check is done where the value takes effect, not where it is assigned. mddev->dev_sectors is written from several places, and none of them can tell whether the value is usable: the chunk size may still change later. raid5_run() and raid5_resize() are where every input is final, so one check in each covers them all. Fixes: eea136d69f9f ("md: fix buglet in RAID5 -> RAID0 conversion.") Reported-by: syzbot+50f9bc7ab8eb224b7629@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=50f9bc7ab8eb224b7629 Signed-off-by: Zizhi Wo Link: https://patch.msgid.link/20260916095137.1157801-1-wozizhi@huaweicloud.com Signed-off-by: Yu Kuai --- drivers/md/raid5.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 45b00c990421..fbc356e44245 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -8213,6 +8213,12 @@ static int raid5_run(struct mddev *mddev) /* device size must be a multiple of chunk size */ mddev->dev_sectors &= ~((sector_t)mddev->chunk_sectors - 1); + if (!mddev->dev_sectors) { + pr_warn("md/raid:%s: device size is smaller than one chunk\n", + mdname(mddev)); + ret = -EINVAL; + goto abort; + } mddev->resync_max_sectors = mddev->dev_sectors; if (mddev->degraded > dirty_parity_disks && @@ -8546,6 +8552,8 @@ static int raid5_resize(struct mddev *mddev, sector_t sectors) if (raid5_has_log(conf) || raid5_has_ppl(conf)) return -EINVAL; sectors &= ~((sector_t)conf->chunk_sectors - 1); + if (!sectors) + return -EINVAL; newsize = raid5_size(mddev, sectors, mddev->raid_disks); if (mddev->external_size && mddev->array_sectors > newsize)