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: 4d02h, 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
Based on kernel commit 067c5c24cef2:

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

  1. 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.
  2. 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.
  3. 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.
  4. Out-of-Bounds Read in super_90_validate:

    • super_types[0].validate_super invokes super_90_validate() (drivers/md/md.c:1492).
    • super_90_validate() assumes rdev->sb_page holds a valid 0.90 superblock:
      mdp_disk_t *desc;
      mdp_super_t *sb = page_address(rdev->sb_page);
      ...
      desc = sb->disks + rdev->desc_nr;
      
      if (desc->state & (1<<MD_DISK_FAULTY))
      
    • In mdp_super_t (include/uapi/linux/raid/md_p.h), sb->disks has array dimension MD_SB_DISKS (27), starting at byte offset 0x200 (MD_SB_DISKS_OFFSET * 4). Each descriptor is 128 bytes (sizeof(mdp_disk_t) = 32 * 4), and offsetof(mdp_disk_t, state) is 16 (0x10).
    • The address evaluated is (char *)sb + 0x210 + (rdev->desc_nr * 128).
    • With rdev->desc_nr = 35, the calculation yields 0x210 + (35 * 0x80) = 0x1390, which is 0x390 bytes past the 4096-byte boundary (0x1000) of rdev->sb_page.
    • In the direct map, this indexes into whatever physical page happens to be adjacent to rdev->sb_page. In the Syzkaller report, the adjacent page was an allocated slab page belonging to skbuff_head_cache, tripping KASAN's redzone checks and producing slab-out-of-bounds Read of size 4 at addr ffff888062348390.
▶ ▼ ✨ AI Jobs (2)
ID Workflow Result Correct Bug Created Started Finished Revision
5eee6af1 repro-c Reproduced: ✅ ❓ KASAN: slab-out-of-bounds Read in super_90_validate 2026/11/14 06:03 2026/11/14 06:03 2026/11/14 07:19 819ebe34
5a62d948 assessment-security DenialOfService: ✅ Exploitable: ✅ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ✅ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KASAN: slab-out-of-bounds Read in super_90_validate 2026/11/11 21:13 2026/11/11 21:13 2026/11/11 21:52 8d68b303
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [raid?] KASAN: slab-out-of-bounds Read in super_90_validate 0 (1) 2026/11/15 22:06
▶ ▼ Similar bugs (1)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
linux-6.1 UBSAN: array-index-out-of-bounds in super_90_validate 15 syz, C 5 7d17h 8d04h 0/3 upstream: reported C repro on 2026/11/07 18:56
▶ ▼ Last patch testing requests (1)
Created Duration User Patch Repo Result
2026/11/14 08:11 26m retest repro git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2800868f63227c8237d70c41e70d441c919620d6 report job log log

Sample crash report:
md: md0 stopped.
md/raid1:md0: active with 1 out of 35 mirrors
md0: detected capacity change from 0 to 4096
=========================
==================================================================
BUG: KASAN: use-after-free in super_90_validate+0x178b/0x18c0 drivers/md/md.c:1592
Read of size 4 at addr ffff888073d36390 by task syz-executor291/6009

CPU: 1 UID: 0 PID: 6009 Comm: syz-executor291 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0x13d/0x4b0 mm/kasan/report.c:482
 kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
 super_90_validate+0x178b/0x18c0 drivers/md/md.c:1592
 add_bound_rdev+0x274/0x390 drivers/md/md.c:3012
 md_add_new_disk+0xca1/0x1820 drivers/md/md.c:7666
 md_ioctl+0x2c1d/0x3700 drivers/md/md.c:8525
 blkdev_ioctl+0x5ad/0x6f0 block/ioctl.c:798
 vfs_ioctl fs/ioctl.c:52 [inline]
 __do_sys_ioctl fs/ioctl.c:598 [inline]
 __se_sys_ioctl fs/ioctl.c:584 [inline]
 __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:584
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x123/0x7b0 arch/x86/entry/syscall_64.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f27a95100ab
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ffd4383a800 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007f27a95100ab
RDX: 00007ffd4383a890 RSI: 0000000040140921 RDI: 0000000000000003
RBP: 00007ffd4383a890 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f27a956cfc7
R13: 0000000000000002 R14: 00007f27a9594cc0 R15: 0000000000000002
 </TASK>

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x73d36
flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
page_type: f0(buddy)
raw: 00fff00000000000 ffffea0000d5ae88 ffffea0000d5af88 0000000000000000
raw: 0000000000000000 0000000000000000 00000000f0000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as freed
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xcc0(GFP_KERNEL), pid 5639, tgid 5639 (syz-executor), ts 63888746915
 set_page_owner include/linux/page_owner.h:33 [inline]
 post_alloc_hook+0xfd/0x120 mm/page_alloc.c:1860
 prep_new_page mm/page_alloc.c:1868 [inline]
 get_page_from_freelist+0xdae/0x2c80 mm/page_alloc.c:3950
 __alloc_frozen_pages_noprof+0x2ad/0x31e0 mm/page_alloc.c:5492
 __alloc_pages_noprof mm/page_alloc.c:5532 [inline]
 alloc_pages_bulk_noprof+0x77e/0x1420 mm/page_alloc.c:5349
 ___alloc_pages_bulk mm/kasan/shadow.c:345 [inline]
 __kasan_populate_vmalloc_do mm/kasan/shadow.c:370 [inline]
 __kasan_populate_vmalloc+0xf0/0x210 mm/kasan/shadow.c:424
 kasan_populate_vmalloc include/linux/kasan.h:580 [inline]
 alloc_vmap_area+0x963/0x2bc0 mm/vmalloc.c:2151
 __get_vm_area_node+0x1ed/0x350 mm/vmalloc.c:3281
 __vmalloc_node_range_noprof+0x2ad/0x1440 mm/vmalloc.c:4112
 vmalloc_user_noprof+0x9e/0xe0 mm/vmalloc.c:4307
 kcov_ioctl+0x4d/0x750 kernel/kcov.c:756
 vfs_ioctl fs/ioctl.c:52 [inline]
 __do_sys_ioctl fs/ioctl.c:598 [inline]
 __se_sys_ioctl fs/ioctl.c:584 [inline]
 __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:584
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x123/0x7b0 arch/x86/entry/syscall_64.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
page last free pid 5356 tgid 5356 ts 65501624162 stack trace:
 reset_page_owner include/linux/page_owner.h:26 [inline]
 __free_pages_prepare mm/page_alloc.c:1402 [inline]
 __free_frozen_pages+0x7b1/0x10f0 mm/page_alloc.c:2958
 kasan_depopulate_vmalloc_pte+0x5d/0x80 mm/kasan/shadow.c:484
 apply_to_pte_range mm/memory.c:3536 [inline]
 apply_to_pmd_range mm/memory.c:3580 [inline]
 apply_to_pud_range mm/memory.c:3616 [inline]
 apply_to_p4d_range mm/memory.c:3652 [inline]
 __apply_to_page_range+0xb1d/0x1520 mm/memory.c:3688
 __kasan_release_vmalloc+0xd7/0xe0 mm/kasan/shadow.c:602
 kasan_release_vmalloc include/linux/kasan.h:593 [inline]
 kasan_release_vmalloc_node mm/vmalloc.c:2312 [inline]
 purge_vmap_node+0x210/0xb40 mm/vmalloc.c:2334
 __purge_vmap_area_lazy+0x90a/0xc00 mm/vmalloc.c:2424
 drain_vmap_area_work+0x27/0x40 mm/vmalloc.c:2458
 process_one_work+0xac7/0x1b10 kernel/workqueue.c:3495
 process_scheduled_works kernel/workqueue.c:3578 [inline]
 worker_thread+0x5ef/0xe50 kernel/workqueue.c:3659
 kthread+0x373/0x450 kernel/kthread.c:436
 ret_from_fork+0x730/0xd60 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245

Memory state around the buggy address:
 ffff888073d36280: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ffff888073d36300: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
>ffff888073d36380: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
                         ^
 ffff888073d36400: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ffff888073d36480: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================

Crashes (3):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/14 08:38 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2800868f6322 8d68b303 .config console log report C ci-upstream-kasan-gce-selinux-root KASAN: use-after-free Read in super_90_validate
2026/11/11 21:13 upstream 067c5c24cef2 8d68b303 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-kasan-gce-selinux-root KASAN: slab-out-of-bounds Read in super_90_validate
2026/11/14 11:36 upstream 2800868f6322 819ebe34 .config console log report info [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu-upstream KASAN: slab-use-after-free Read in super_90_validate
* Struck through repros no longer work on HEAD.