BUG: unable to handle kernel NULL pointer dereference in dm_bufio_client_create

Status: upstream: reported C repro on 2026/11/01 18:25
Subsystems: dm
Labels: prio:normal
[Documentation on labels]
Reported-by: syzbot+41e0d7cdfb1ecfc4a007@syzkaller.appspotmail.com
First crash: 1d13h, last: 1d10h
✨ AI-generated root cause summary:

In drivers/md/dm-thin.c, pool_ctr() attempts to ensure metadata and data devices are distinct by checking strcmp(argv[0], argv[1]), which fails when different path strings resolve to the same device. When the pool is created with read_only mode, dm_get_device() opens the metadata device as read-only, and the subsequent open of the data device as read-write invokes upgrade_mode() in drivers/md/dm-table.c, which frees the old table device via dm_put_table_device(). This leaves metadata_dev as a dangling pointer (use-after-free); when metadata_dev->bdev is subsequently read as NULL from the freed slab entry and passed to dm_bufio_client_create(), dereferencing bdev->bd_dev causes an immediate kernel NULL pointer dereference.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit abf8c33470f7:

Root Cause Analysis

1. Functions, Structs, and Files Involved

  • drivers/md/dm-thin.c: pool_ctr()
  • drivers/md/dm-table.c: dm_get_device(), upgrade_mode()
  • drivers/md/dm.c: dm_put_table_device(), close_table_device()
  • drivers/md/dm-bufio.c: dm_bufio_client_create()
  • Structs & Fields:
    • struct dm_dev (bdev, mode)
    • struct table_device (count, dm_dev, list)
    • struct dm_dev_internal (count, dm_dev)
    • struct block_device (bd_dev at offset 0x24 / 36 bytes on 32-bit ARM)

2. Triggering Mechanism and Invariant Violation

  1. Flawed Disjoint-Device Validation in pool_ctr: In drivers/md/dm-thin.c:pool_ctr(), thin pools require that the metadata device (argv[0]) and data device (argv[1]) be separate, distinct block devices. The constructor checks:

    /* make sure metadata and data are different devices */
    if (!strcmp(argv[0], argv[1])) {
        ti->error = "Error setting metadata or data device";
        r = -EINVAL;
        goto out_unlock;
    }
    

    This string check (!strcmp) is insufficient. If a user supplies two syntactically different path strings (e.g. /dev/sda and 8:0, or symlinks / alternative device node paths) that resolve to the same underlying dev_t / block device, the check passes.

  2. Mode Asymmetry and upgrade_mode(): When the read_only pool feature is requested (pf.mode == PM_READ_ONLY), the metadata device is opened with metadata_mode = BLK_OPEN_READ:

    r = dm_get_device(ti, argv[0], metadata_mode, &metadata_dev);
    

    This allocates and returns a struct table_device td (holding td->dm_dev) with td->count == 1. At this moment, metadata_dev->bdev is valid, allowing warn_if_metadata_device_too_big(metadata_dev->bdev) to succeed.

    Next, pool_ctr() opens the data device with read-write access:

    r = dm_get_device(ti, argv[1], BLK_OPEN_READ | BLK_OPEN_WRITE, &data_dev);
    

    Because both paths resolve to the same dev_t, dm_get_device() locates the existing struct dm_dev_internal dd in ti->table->devices. Finding that the existing mode is BLK_OPEN_READ while the newly requested mode is BLK_OPEN_READ | BLK_OPEN_WRITE, it invokes upgrade_mode(dd, mode, t->md).

  3. Use-After-Free via upgrade_mode(): In drivers/md/dm-table.c:upgrade_mode():

    old_dev = dd->dm_dev;
    r = dm_get_table_device(md, dd->dm_dev->bdev->bd_dev,
                            dd->dm_dev->mode | new_mode, &new_dev);
    if (r)
        return r;
    
    dd->dm_dev = new_dev;
    dm_put_table_device(md, old_dev);
    

    dm_get_table_device() looks for an entry in md->table_devices matching (dev, mode). Because the mode differs, it opens a new struct table_device (new_dev). Then, dm_put_table_device(md, old_dev) decrements the refcount of old_dev. Since td->count drops to zero, close_table_device() is called, unlinking and freeing td via kfree(td).

    However, metadata_dev in pool_ctr() still points directly to old_dev (&td->dm_dev). It is now a dangling pointer to freed slab memory.

  4. Propagation to dm_bufio_client_create and Crash: Later in pool_ctr():

    pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev, data_dev->bdev,
                       block_size, pf.mode == PM_READ_ONLY, &ti->error, &pool_created);
    

    Reading metadata_dev->bdev performs a read from freed memory. Because the slab entry was cleared / zeroed upon free, metadata_dev->bdev evaluates to NULL (0x00000000).

    This NULL pointer is passed down the call chain: pool_create() $\to$ dm_pool_metadata_open() $\to$ dm_block_manager_create() $\to$ dm_bufio_client_create(bdev, ...).

    In drivers/md/dm-bufio.c:dm_bufio_client_create():

    c->shrinker = shrinker_alloc(0, "dm-bufio:(%u:%u)",
                                 MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
    

    Evaluating bdev->bd_dev reads offset 36 (0x24) from NULL (r3: 00000000), generating the instruction ldr r2, [r3, #36] and triggering the observed kernel NULL pointer dereference crash.


3. Runtime Consequences in a Production Kernel

  • MemoryCorruption: true The fundamental underlying defect is a Use-After-Free (UAF). While in this specific execution the freed memory was zeroed (leading to a NULL pointer read), in general production conditions the memory of the freed struct table_device can be reallocated to another object or retain stale/corrupted pointer data. Reading metadata_dev->bdev would then dereference a wild or controlled pointer. Furthermore, destructor cleanup paths such as pool_dtr() invoke dm_put_device(ti, pt->metadata_dev) on the freed device pointer.
  • RawDenialOfService: true The NULL pointer dereference (or wild pointer dereference) in dm_bufio_client_create() causes an immediate kernel oops/panic during table configuration (DM_TABLE_LOAD ioctl).
  • InfoLeak: none The bug does not copy freed or uninitialized data to userspace or expose kernel memory over the network.
  • IsNetCandidate: false / IsKVMCandidate: false The defect is situated entirely within the block / Device Mapper (drivers/md) subsystem and is not related to networking or KVM/virtualization.
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
c713b581 assessment-security DenialOfService: ✅ Exploitable: ✅ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ✅ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ BUG: unable to handle kernel NULL pointer dereference in dm_bufio_client_create 2026/10/31 09:11 2026/10/31 09:11 2026/10/31 09:35 2041d3f8
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [dm?] BUG: unable to handle kernel NULL pointer dereference in dm_bufio_client_create 0 (1) 2026/11/01 18:25

Sample crash report:
8<--- cut here ---
Unable to handle kernel NULL pointer dereference at virtual address 00000024 when read
[00000024] *pgd=86ce2003, *pmd=df568003
Internal error: Oops: 205 [#1] SMP ARM
Modules linked in:
CPU: 0 UID: 0 PID: 4130 Comm: syz.2.17 Not tainted syzkaller #0 PREEMPT 
Hardware name: ARM-Versatile Express
PC is at dm_bufio_client_create+0x298/0x5fc drivers/md/dm-bufio.c:2585
LR is at __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:211 [inline]
LR is at _raw_spin_unlock_irqrestore+0x30/0x54 kernel/locking/spinlock.c:221
pc : [<812f4044>]    lr : [<81c19680>]    psr: 60000013
sp : df9f1b30  ip : df9f1aa8  fp : df9f1bd4
r10: 00000000  r9 : df9f1b48  r8 : 837359c4
r7 : 83735984  r6 : 8633b638  r5 : 00000000  r4 : 8633b600
r3 : 00000000  r2 : 00000000  r1 : ffc00000  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 30c5387d  Table: 86a96cc0  DAC: fffffffd
Register r0 information: NULL pointer
Register r1 information: non-paged memory
Register r2 information: NULL pointer
Register r3 information: NULL pointer
Register r4 information: slab kmalloc-512 start 8633b600 pointer offset 0 size 512
Register r5 information: NULL pointer
Register r6 information: slab kmalloc-512 start 8633b600 pointer offset 56 size 512
Register r7 information: non-slab/vmalloc memory
Register r8 information: non-slab/vmalloc memory
Register r9 information: 2-page vmalloc region starting at 0xdf9f0000 allocated at kernel_clone+0xc4/0x440 kernel/fork.c:2784
Register r10 information: NULL pointer
Register r11 information: 2-page vmalloc region starting at 0xdf9f0000 allocated at kernel_clone+0xc4/0x440 kernel/fork.c:2784
Register r12 information: 2-page vmalloc region starting at 0xdf9f0000 allocated at kernel_clone+0xc4/0x440 kernel/fork.c:2784
Process syz.2.17 (pid: 4130, stack limit = 0xdf9f0000)
Stack: (0xdf9f1b30 to 0xdf9f2000)
1b20:                                     00000001 00000000 00001fff 00000006
1b40: 00000000 00000008 00000008 00000000 00000000 00000000 00000000 00000000
1b60: 00000000 625f6d64 6f696675 6675625f 2d726566 00312d38 00000000 00000000
1b80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1ba0: 00000000 5d3b91d7 80579764 863f8c80 00000000 00001000 00000006 00000000
1bc0: 00000000 00000800 df9f1c0c df9f1bd8 8130d108 812f3db8 8130d020 8130d03c
1be0: 00000000 868f1040 00000000 868f0c00 00000000 00000000 00000000 00000000
1c00: df9f1c34 df9f1c10 81329660 8130d0a4 868f1040 8276076c 826a9678 00000000
1c20: 83735a70 00000000 df9f1cb4 df9f1c38 81322274 813295d4 00000000 00000000
1c40: 835c30a0 863f87c0 8417c000 86852000 83735a60 00000003 863f894c 00000800
1c60: 863f87cc 00000000 00000014 00000000 812e5f44 00000000 00000007 00000000
1c80: 863f8858 5d3b91d7 00000000 863c7180 868f1040 00000000 00000000 00000800
1ca0: 00000000 00000000 df9f1d0c df9f1cb8 812e623c 81321df0 00000008 df9f1d2c
1cc0: 868f1040 00000000 85b2db60 85b2db50 df9f1d0c 00000006 863f8840 5d3b91d7
1ce0: 86852000 0000006f 85b2db38 85b2db60 85b2da00 00000000 85b2dba8 86852000
1d00: df9f1d64 df9f1d10 812ec884 812e60b0 00000800 00000000 85b2db60 85b2da00
1d20: 812eabcc 863c7180 00000000 863c7180 00000000 5d3b91d7 df9f1d54 812ec740
1d40: 85b2da00 200006c0 863f0e80 00000009 000001a8 000001a8 df9f1f04 df9f1d68
1d60: 812eaa04 812ec74c df9f1e74 81ee19d4 8259d20c 8088542c 81ee1718 00000077
1d80: 85b2db31 b5403587 00000000 00000000 00000001 00000000 00000004 00000032
1da0: 00000000 000001a8 00000138 00000001 00000000 00020000 00000000 00000000
1dc0: 0000f800 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1de0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1e00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1e20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1e40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1e60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1e80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1ea0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1ec0: 00000000 00000000 00000000 00000000 00000003 5d3b91d7 df9f1ef4 00000000
1ee0: 863f0e80 c138fd09 200006c0 863f0e80 00000003 84d3be80 df9f1f14 df9f1f08
1f00: 812eaf64 812ea79c df9f1fa4 df9f1f18 805f2e84 812eaf60 df9f1f4c df9f1f28
1f20: 8035d380 8036088c ffffffff 81c0c4c0 df9f1f54 df9f1f40 8025cbac 00000001
1f40: df9f1fa4 df9f1f50 8035dc54 8035d1f8 00000000 00000001 ffffffff 8341a10c
1f60: 00000000 00000000 00000000 00000000 00000000 5d3b91d7 df9f1fac 00000000
1f80: 00000000 00356310 00000036 8020029c 84d3be80 00000036 00000000 df9f1fa8
1fa0: 80200060 805f2c4c 00000000 00000000 00000003 c138fd09 200006c0 00000000
1fc0: 00000000 00000000 00356310 00000036 003562d8 00000000 00000008 00000000
1fe0: 7ecaa7a0 7ecaa790 0001903c 00130cf0 60000010 00000003 00000000 00000000
Call trace: 
[<812f3dac>] (dm_bufio_client_create) from [<8130d108>] (dm_block_manager_create+0x70/0xb0 drivers/md/persistent-data/dm-block-manager.c:397)
 r10:00000800 r9:00000000 r8:00000000 r7:00000006 r6:00001000 r5:00000000
 r4:863f8c80
[<8130d098>] (dm_block_manager_create) from [<81329660>] (__create_persistent_data_objects drivers/md/dm-thin-metadata.c:791 [inline])
[<8130d098>] (dm_block_manager_create) from [<81329660>] (dm_pool_metadata_open+0x98/0x15c drivers/md/dm-thin-metadata.c:980)
 r8:00000000 r7:00000000 r6:00000000 r5:00000000 r4:868f0c00
[<813295c8>] (dm_pool_metadata_open) from [<81322274>] (pool_create drivers/md/dm-thin.c:2946 [inline])
[<813295c8>] (dm_pool_metadata_open) from [<81322274>] (__pool_find drivers/md/dm-thin.c:3113 [inline])
[<813295c8>] (dm_pool_metadata_open) from [<81322274>] (pool_ctr+0x490/0xa64 drivers/md/dm-thin.c:3363)
 r9:00000000 r8:83735a70 r7:00000000 r6:826a9678 r5:8276076c r4:868f1040
[<81321de4>] (pool_ctr) from [<812e623c>] (dm_table_add_target+0x198/0x3bc drivers/md/dm-table.c:749)
 r10:00000000 r9:00000000 r8:00000800 r7:00000000 r6:00000000 r5:868f1040
 r4:863c7180
[<812e60a4>] (dm_table_add_target) from [<812ec884>] (populate_table drivers/md/dm-ioctl.c:1602 [inline])
[<812e60a4>] (dm_table_add_target) from [<812ec884>] (table_load+0x144/0x3d4 drivers/md/dm-ioctl.c:1645)
 r10:86852000 r9:85b2dba8 r8:00000000 r7:85b2da00 r6:85b2db60 r5:85b2db38
 r4:0000006f
[<812ec740>] (table_load) from [<812eaa04>] (ctl_ioctl+0x274/0x7c4 drivers/md/dm-ioctl.c:2221)
 r10:000001a8 r9:000001a8 r8:00000009 r7:863f0e80 r6:200006c0 r5:85b2da00
 r4:812ec740
[<812ea790>] (ctl_ioctl) from [<812eaf64>] (dm_ctl_ioctl+0x10/0x14 drivers/md/dm-ioctl.c:2243)
 r10:84d3be80 r9:00000003 r8:863f0e80 r7:200006c0 r6:c138fd09 r5:863f0e80
 r4:00000000
[<812eaf54>] (dm_ctl_ioctl) from [<805f2e84>] (vfs_ioctl fs/ioctl.c:51 [inline])
[<812eaf54>] (dm_ctl_ioctl) from [<805f2e84>] (do_vfs_ioctl fs/ioctl.c:551 [inline])
[<812eaf54>] (dm_ctl_ioctl) from [<805f2e84>] (__do_sys_ioctl fs/ioctl.c:595 [inline])
[<812eaf54>] (dm_ctl_ioctl) from [<805f2e84>] (sys_ioctl+0x244/0xb5c fs/ioctl.c:583)
[<805f2c40>] (sys_ioctl) from [<80200060>] (ret_fast_syscall+0x0/0x1c arch/arm/mm/proc-v7.S:67)
Exception stack(0xdf9f1fa8 to 0xdf9f1ff0)
1fa0:                   00000000 00000000 00000003 c138fd09 200006c0 00000000
1fc0: 00000000 00000000 00356310 00000036 003562d8 00000000 00000008 00000000
1fe0: 7ecaa7a0 7ecaa790 0001903c 00130cf0
 r10:00000036 r9:84d3be80 r8:8020029c r7:00000036 r6:00356310 r5:00000000
 r4:00000000
Code: e3a05000 e3a01000 e34f1fc0 e1a00005 (e5932024) 
---[ end trace 0000000000000000 ]---
----------------
Code disassembly (best guess):
   0:	e3a05000 	mov	r5, #0
   4:	e3a01000 	mov	r1, #0
   8:	e34f1fc0 	movt	r1, #65472	@ 0xffc0
   c:	e1a00005 	mov	r0, r5
* 10:	e5932024 	ldr	r2, [r3, #36]	@ 0x24 <-- trapping instruction

Crashes (4):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/10/31 12:08 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux master abf8c33470f7 2041d3f8 .config console log report syz / log C [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu2-arm32 BUG: unable to handle kernel NULL pointer dereference in dm_bufio_client_create
2026/10/31 11:10 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux master abf8c33470f7 2041d3f8 .config console log report syz / log C [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu2-arm32 BUG: unable to handle kernel NULL pointer dereference in dm_bufio_client_create
2026/10/31 10:10 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux master abf8c33470f7 2041d3f8 .config console log report syz / log C [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu2-arm32 BUG: unable to handle kernel NULL pointer dereference in dm_bufio_client_create
2026/10/31 09:11 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux master abf8c33470f7 2041d3f8 .config console log report info [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu2-arm32 BUG: unable to handle kernel NULL pointer dereference in dm_bufio_client_create
* Struck through repros no longer work on HEAD.