syzbot
KASAN: slab-out-of-bounds Read in super_90_validate
Status:
upstream: reported C repro on 2026/11/15 22:06
Subsystems:
raid
Labels:
prio:normal
[Documentation on labels]
Reported-by: syzbot+c5969d7e0a934b384fd6@syzkaller.appspotmail.com
First crash: 4d01h, last: 1d11h
✨ AI-generated root cause summary:
When creating a non-persistent RAID array (not_persistent = 1) via SET_ARRAY_INFO (md_set_array_info()), mddev->max_disks is left uninitialized as 0. When a new disk is added via md_add_new_disk() (ADD_NEW_DISK), mddev->persistent == 0 causes md_import_device() to allocate an uninitialized rdev->sb_page without loading superblock data, and bind_rdev_to_array() assigns rdev->desc_nr from mddev->raid_disks without enforcing MD_SB_DISKS (since max_disks == 0). For array personalities lacking hot_remove_disk (such as md-linear) or when journal devices are added, add_bound_rdev() unconditionally calls super_types[mddev->major_version].validate_super(), executing super_90_validate() without checking mddev->persistent. In super_90_validate(), desc = sb->disks + rdev->desc_nr is accessed without checking that rdev->desc_nr < MD_SB_DISKS (27), leading to an out-of-bounds read beyond the 4KB superblock page into adjacent kernel memory.
▶
▼
✨ Full AI root cause analysis
Root Cause Analysis
1. Functions, Structs, and Files Involved
- File:
drivers/md/md.c
- Functions:
add_bound_rdev(), super_90_validate(), bind_rdev_to_array(), md_add_new_disk(), md_set_array_info()
- Structs and Fields:
struct mddev: persistent, major_version, raid_disks, max_disks, pers
struct md_rdev: desc_nr, raid_disk, sb_page, flags
mdp_super_t (struct mdp_superblock_s): disks[MD_SB_DISKS] (MD_SB_DISKS = 27)
mdp_disk_t (struct mdp_device_descriptor_s): state, raid_disk
2. Technical Mechanism and Triggering Flow
-
Non-persistent Array Configuration (md_set_array_info):
- When a RAID array is initialized via the
SET_ARRAY_INFO ioctl with not_persistent = 1 (such as a linear or non-persistent RAID array), md_set_array_info() (drivers/md/md.c:7932) sets:
mddev->major_version = MD_MAJOR_VERSION (0)
mddev->persistent = !info->not_persistent (0)
mddev->raid_disks = info->raid_disks
- However,
mddev->max_disks = MD_SB_DISKS is only set conditionally if (mddev->persistent). Consequently, for a non-persistent array, mddev->max_disks remains 0.
-
Hot Adding a Disk (md_add_new_disk):
- When adding a device via
md_add_new_disk() (drivers/md/md.c:7518) to an active array (mddev->pers != NULL):
- Because
mddev->persistent == 0, rdev = md_import_device(dev, -1, -1) is invoked. This allocates rdev->sb_page via alloc_page(GFP_KERNEL) but does not read or initialize any superblock from the disk.
rdev->desc_nr is initialized to -1 by md_rdev_init().
- In
bind_rdev_to_array() (drivers/md/md.c:2609), because rdev->desc_nr < 0, the kernel allocates a free slot:
int choice = 0;
if (mddev->pers)
choice = mddev->raid_disks;
while (md_find_rdev_nr_rcu(mddev, choice))
choice++;
rdev->desc_nr = choice;
- For an array configured with
mddev->raid_disks >= 27 (or hot-adding disks past disk 26), rdev->desc_nr becomes >= MD_SB_DISKS (e.g., 35).
- Furthermore,
bind_rdev_to_array() checks:
if (!test_bit(Journal, &rdev->flags) &&
mddev->max_disks && rdev->desc_nr >= mddev->max_disks)
Because mddev->max_disks == 0 for non-persistent arrays, this bound check is skipped, allowing rdev->desc_nr to exceed MD_SB_DISKS.
-
Unconditional validate_super Call in add_bound_rdev:
md_add_new_disk() calls add_bound_rdev(rdev) (drivers/md/md.c:7666).
- In
add_bound_rdev() (drivers/md/md.c:3007):
if (!mddev->pers->hot_remove_disk || add_journal) {
super_types[mddev->major_version].
validate_super(mddev, NULL/*freshest*/, rdev);
err = mddev->pers->hot_add_disk(mddev, rdev);
- For array personalities that do not provide
.hot_remove_disk (such as md-linear, drivers/md/md-linear.c), or when adding a journal disk, add_bound_rdev() calls validate_super() unconditionally, without checking whether mddev->persistent is true.
-
Out-of-Bounds Read in super_90_validate:
▶
▼
Last patch testing requests (1)
Sample crash report:
* Struck through repros no longer work on HEAD.