From: Chandradhar Kumar <chandradhar.2003@gmail.com>
To: Song Liu <song@kernel.org>, Yu Kuai <yukuai@fygo.io>,
Li Nan <magiclinan@didiglobal.com>, Xiao Ni <xiao@kernel.org>,
linux-raid@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Chandradhar Kumar <chandradhar.2003@gmail.com>,
syzbot+9e3014263a35700ab49b@syzkaller.appspotmail.com
Subject: [PATCH v2] md: enforce MD_SB_DISKS bounds across v0.90 superblock paths
Date: Sat, 14 Nov 2026 06:36:23 +0530 [thread overview]
Message-ID: <20261114010635.6048-1-chandradhar.2003@gmail.com> (raw)
The legacy v0.90 superblock format (mdp_super_t) embeds a fixed-size
array of MD_SB_DISKS (27) disk descriptors (sb->disks[27]). Currently,
several configuration and metadata load paths fail to enforce this
upper bound, allowing mddev->raid_disks, rdev->raid_disk, or
rdev->desc_nr to exceed MD_SB_DISKS - 1 and trigger an out-of-bounds
array access in super_90_sync():
UBSAN: array-index-out-of-bounds in drivers/md/md.c:1740:29
index 124 is out of range for type 'mdp_disk_t [27]'
Fix this across all entry points and harden super_90_sync():
1. In super_90_load(), reject superblocks with sb->raid_disks >
MD_SB_DISKS or sb->this_disk.number >= MD_SB_DISKS, and guard
rdev->desc_nr in super_90_validate() before indexing sb->disks[].
2. In metadata_store(), validate existing mddev->raid_disks against
MD_SB_DISKS when major == 0 and initialize mddev->max_disks so
sysfs-configured v0.90 arrays enforce the 27-disk limit regardless
of the order in which raid_disks and metadata_version are written.
3. In raid_disks_store(), slot_store(), and md_set_array_info(),
reject disk counts or slot indices that exceed MD_SB_DISKS or
mddev->max_disks across all configuration and reshape branches.
4. In super_90_sync(), bounds-check desc_nr and mddev->raid_disks
before indexing sb->disks[], and synthesize a faulty/removed
this_disk descriptor if rdev->desc_nr is out of range rather than
indexing out-of-bounds or zeroing this_disk (which would alias
slot 0).
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+9e3014263a35700ab49b@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9e3014263a35700ab49b
Signed-off-by: Chandradhar Kumar <chandradhar.2003@gmail.com>
---
Changes in v2:
- Drop early return in super_90_sync() so rdev->sb_size and sb_page are
properly initialized before sync_sbs() sets rdev->sb_loaded = 1.
- Do not reset rdev2->desc_nr = -1 or zero sb->this_disk on skip; instead
populate sb->this_disk as faulty/removed with rdev->desc_nr so a skipped
device cannot alias slot 0 on reassembly.
- Validate mddev->raid_disks and set mddev->max_disks = MD_SB_DISKS in
metadata_store() when major == 0, and move the raid_disks_store() check
above the reshape/active branches to cover all sysfs paths.
- Remove the info->major_version == 0 condition in md_set_array_info().
- Drop redundant unsigned < 0 check on sb->this_disk.number in
super_90_load() and guard rdev->desc_nr in super_90_validate().
drivers/md/md.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 55 insertions(+), 3 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 03e35386a802..71f536d6257c 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1407,7 +1407,8 @@ static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor
goto abort;
}
- if (sb->raid_disks <= 0 || sb->raid_disks > MD_SB_DISKS)
+ if (sb->raid_disks <= 0 || sb->raid_disks > MD_SB_DISKS ||
+ sb->this_disk.number >= MD_SB_DISKS)
goto abort;
if (md_csum_fold(calc_sb_csum(sb)) != md_csum_fold(sb->sb_csum)) {
@@ -1587,6 +1588,8 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *freshest, stru
return 0;
}
+ if (rdev->desc_nr < 0 || rdev->desc_nr >= MD_SB_DISKS)
+ return -EINVAL;
desc = sb->disks + rdev->desc_nr;
if (desc->state & (1<<MD_DISK_FAULTY))
@@ -1708,6 +1711,12 @@ static void super_90_sync(struct mddev *mddev, struct md_rdev *rdev)
else
desc_nr = next_spare++;
rdev2->desc_nr = desc_nr;
+
+ if (desc_nr < 0 || desc_nr >= MD_SB_DISKS) {
+ pr_warn("md: %s: desc_nr %d out of range for rdev %pg, skipping\n",
+ mdname(mddev), desc_nr, rdev2->bdev);
+ continue;
+ }
d = &sb->disks[rdev2->desc_nr];
nr_disks++;
d->number = rdev2->desc_nr;
@@ -1736,7 +1745,7 @@ static void super_90_sync(struct mddev *mddev, struct md_rdev *rdev)
d->state |= (1<<MD_DISK_FAILFAST);
}
/* now set the "removed" and "faulty" bits on any missing devices */
- for (i=0 ; i < mddev->raid_disks ; i++) {
+ for (i = 0 ; i < mddev->raid_disks && i < MD_SB_DISKS; i++) {
mdp_disk_t *d = &sb->disks[i];
if (d->state == 0 && d->number == 0) {
d->number = i;
@@ -1752,7 +1761,17 @@ static void super_90_sync(struct mddev *mddev, struct md_rdev *rdev)
sb->failed_disks = failed;
sb->spare_disks = spare;
- sb->this_disk = sb->disks[rdev->desc_nr];
+ if (rdev->desc_nr >= 0 && rdev->desc_nr < MD_SB_DISKS) {
+ sb->this_disk = sb->disks[rdev->desc_nr];
+ } else {
+ sb->this_disk.number = rdev->desc_nr;
+ sb->this_disk.major = MAJOR(rdev->bdev->bd_dev);
+ sb->this_disk.minor = MINOR(rdev->bdev->bd_dev);
+ sb->this_disk.raid_disk = rdev->desc_nr;
+ sb->this_disk.state = (1 << MD_DISK_FAULTY) |
+ (1 << MD_DISK_REMOVED);
+ }
+
sb->sb_csum = calc_sb_csum(sb);
}
@@ -3353,6 +3372,12 @@ slot_store(struct md_rdev *rdev, const char *buf, size_t len)
if (slot >= rdev->mddev->raid_disks &&
slot >= rdev->mddev->raid_disks + rdev->mddev->delta_disks)
return -ENOSPC;
+ if ((rdev->mddev->max_disks &&
+ slot >= rdev->mddev->max_disks) ||
+ (rdev->mddev->persistent &&
+ rdev->mddev->major_version == 0 &&
+ slot >= MD_SB_DISKS))
+ return -ENOSPC;
rdev->raid_disk = slot;
if (test_bit(In_sync, &rdev->flags))
@@ -3374,6 +3399,12 @@ slot_store(struct md_rdev *rdev, const char *buf, size_t len)
if (slot >= rdev->mddev->raid_disks &&
slot >= rdev->mddev->raid_disks + rdev->mddev->delta_disks)
return -ENOSPC;
+ if ((rdev->mddev->max_disks &&
+ slot >= rdev->mddev->max_disks) ||
+ (rdev->mddev->persistent &&
+ rdev->mddev->major_version == 0 &&
+ slot >= MD_SB_DISKS))
+ return -ENOSPC;
rdev->raid_disk = slot;
/* assume it is working */
clear_bit(Faulty, &rdev->flags);
@@ -4442,6 +4473,13 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
if (err)
return err;
noio_flags = memalloc_noio_save();
+ if ((mddev->max_disks && n > mddev->max_disks) ||
+ (mddev->persistent && mddev->major_version == 0 &&
+ n > MD_SB_DISKS)) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
if (mddev->pers) {
if (n != mddev->raid_disks)
err = update_raid_disks(mddev, n);
@@ -5029,6 +5067,7 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len)
mddev->external = 0;
mddev->major_version = 0;
mddev->minor_version = 90;
+ mddev->max_disks = 0;
goto out_unlock;
}
if (strncmp(buf, "external:", 9) == 0) {
@@ -5043,6 +5082,7 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len)
mddev->external = 1;
mddev->major_version = 0;
mddev->minor_version = 90;
+ mddev->max_disks = 0;
goto out_unlock;
}
major = simple_strtoul(buf, &e, 10);
@@ -5056,10 +5096,14 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len)
err = -ENOENT;
if (major >= ARRAY_SIZE(super_types) || super_types[major].name == NULL)
goto out_unlock;
+ err = -EINVAL;
+ if (major == 0 && mddev->raid_disks > MD_SB_DISKS)
+ goto out_unlock;
mddev->major_version = major;
mddev->minor_version = minor;
mddev->persistent = 1;
mddev->external = 0;
+ mddev->max_disks = (major == 0) ? MD_SB_DISKS : 0;
err = 0;
out_unlock:
mddev_unlock(mddev);
@@ -7951,6 +7995,14 @@ int md_set_array_info(struct mddev *mddev, struct mdu_array_info_s *info)
mddev->ctime = ktime_get_real_seconds();
return 0;
}
+
+ if (info->raid_disks < 0 ||
+ (!info->not_persistent && info->raid_disks > MD_SB_DISKS)) {
+ pr_warn("md: %s: invalid raid_disks %d for v0.90 superblock\n",
+ mdname(mddev), info->raid_disks);
+ return -EINVAL;
+ }
+
mddev->major_version = MD_MAJOR_VERSION;
mddev->minor_version = MD_MINOR_VERSION;
mddev->patch_version = MD_PATCHLEVEL_VERSION;
--
2.56.0
next reply other threads:[~2026-11-14 1:06 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-14 1:06 Chandradhar Kumar [this message]
2026-11-14 1:32 ` [PATCH v2] md: enforce MD_SB_DISKS bounds across v0.90 superblock paths sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261114010635.6048-1-chandradhar.2003@gmail.com \
--to=chandradhar.2003@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-raid@vger.kernel.org \
--cc=magiclinan@didiglobal.com \
--cc=song@kernel.org \
--cc=syzbot+9e3014263a35700ab49b@syzkaller.appspotmail.com \
--cc=xiao@kernel.org \
--cc=yukuai@fygo.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.