diff --git a/drivers/md/md.c b/drivers/md/md.c index 680b34a63..bcc32b83c 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1393,7 +1393,11 @@ static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor goto abort; } - if (sb->raid_disks <= 0) + /* + * sb->disks[] has only MD_SB_DISKS entries. A larger raid_disks + * count would later make super_90_sync() index past that array. + */ + if (sb->raid_disks <= 0 || sb->raid_disks > MD_SB_DISKS) goto abort; if (md_csum_fold(calc_sb_csum(sb)) != md_csum_fold(sb->sb_csum)) { @@ -1607,6 +1611,18 @@ static void super_90_sync(struct mddev *mddev, struct md_rdev *rdev) struct md_rdev *rdev2; int next_spare = mddev->raid_disks; + /* + * A 0.90 superblock has a fixed-size mdp_disk_t array and uses + * raid_disks as both a loop bound and the first spare disk slot. + * Never index sb->disks[] if an invalid raid_disks value reached + * us through any configuration path. + */ + if (mddev->raid_disks < 0 || mddev->raid_disks > MD_SB_DISKS) { + pr_warn("md: %s: invalid raid_disks %d for 0.90 metadata\n", + mdname(mddev), mddev->raid_disks); + return; + } + /* make rdev->sb match mddev data.. * * 1/ zero out disks @@ -4448,8 +4464,19 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len) mddev->delta_disks = n - olddisks; mddev->raid_disks = n; mddev->reshape_backwards = (mddev->delta_disks < 0); - } else - mddev->raid_disks = n; + } else { + /* + * 0.90 metadata only has room for MD_SB_DISKS descriptors. + * Larger values would make super_90_sync() index beyond + * sb->disks[]. + */ + if ((mddev->persistent && mddev->major_version == 0 && + n > MD_SB_DISKS) || + (mddev->max_disks && n > mddev->max_disks)) + err = -EINVAL; + else + mddev->raid_disks = n; + } out_unlock: memalloc_noio_restore(noio_flags); mddev_unlock_and_resume(mddev); @@ -7926,6 +7953,18 @@ int md_set_array_info(struct mddev *mddev, struct mdu_array_info_s *info) mddev->ctime = ktime_get_real_seconds(); return 0; } + + /* + * Persistent 0.90 metadata stores disks in sb->disks[MD_SB_DISKS]. + * A larger raid_disks count would make super_90_sync() index past + * that array; non-persistent arrays have no such on-disk limit. + */ + if (info->raid_disks < 0 || + (!info->not_persistent && info->raid_disks > MD_SB_DISKS)) { + pr_warn("md: invalid raid_disks %d\n", info->raid_disks); + return -EINVAL; + } + mddev->major_version = MD_MAJOR_VERSION; mddev->minor_version = MD_MINOR_VERSION; mddev->patch_version = MD_PATCHLEVEL_VERSION;