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: eea136d69f ("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 <wozizhi@huawei.com>
Link: https://patch.msgid.link/20260916095137.1157801-1-wozizhi@huaweicloud.com
Signed-off-by: Yu Kuai <yukuai@fygo.io>
This commit is contained in:
Zizhi Wo 2026-09-16 17:51:36 +08:00 committed by Yu Kuai
parent eba5f51870
commit 370ca0760b

View File

@ -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)