mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
eba5f51870
commit
370ca0760b
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user