KCSAN: data-race in block_uevent / inc_diskseq (3)

Status: moderation: reported on 2026/11/11 17:52
Subsystems: block
Labels: prio:low race:benign
[Documentation on labels]
Reported-by: syzbot+128ceaeb0f3fb1084016@syzkaller.appspotmail.com
First crash: 1h48m, last: 1h48m
✨ AI-generated root cause summary:

In disk_force_media_change() (block/disk-events.c), disk_event_uevent() is called before inc_diskseq() to notify userspace of DISK_EVENT_MEDIA_CHANGE. When disk_event_uevent() broadcasts the uevent, it reads disk->diskseq via block_uevent(), emitting the event with the old sequence number. Userspace (udevd) immediately handles the uevent by reading the device's sysfs uevent attribute, which calls block_uevent() concurrently while the task executing disk_force_media_change() proceeds to call inc_diskseq() and writes disk->diskseq, resulting in a KCSAN data race and stale sequence numbers in uevents.

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

Root Cause Analysis

1. Involved Files, Functions, Structs, and Fields


2. Detailed Technical Mechanism

The block layer tracks a monotonically increasing 64-bit disk sequence number (disk->diskseq) to distinguish between different media instances of a block device (e.g. across media changes on removable drives or loop devices). Whenever a media change event occurs, inc_diskseq() allocates a new global sequence number and stores it into disk->diskseq:

void inc_diskseq(struct gendisk *disk)
{
	disk->diskseq = atomic64_inc_return(&diskseq);
}

When notifying user space of device events, the kernel generates a uevent via kobject_uevent_env(), which invokes the block device's class callback block_uevent():

static int block_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
	const struct gendisk *disk = dev_to_disk(dev);

	return add_uevent_var(env, "DISKSEQ=%llu", disk->diskseq);
}

In standard event polling (disk_check_events() in block/disk-events.c), inc_diskseq() is correctly called before disk_event_uevent():

	if (events & DISK_EVENT_MEDIA_CHANGE)
		inc_diskseq(disk);

	if (disk->event_flags & DISK_EVENT_FLAG_UEVENT)
		disk_event_uevent(disk, events);

This ordering guarantees that:

  1. disk->diskseq is updated to the new sequence number before the uevent is prepared and broadcast.
  2. The uevent sent to user space contains the updated DISKSEQ value.

However, in disk_force_media_change() (block/disk-events.c), which is invoked when tearing down or reconfiguring a device (such as __loop_clr_fd() when releasing a loop device), the ordering of operations is inverted:

void disk_force_media_change(struct gendisk *disk)
{
	disk_event_uevent(disk, DISK_EVENT_MEDIA_CHANGE);
	inc_diskseq(disk);
	bdev_mark_dead(disk->part0, true);
}

3. How the Data Race and Stale Sequence Number Manifest

  1. Task 659 (CPU 0): A loop device is cleared in __loop_clr_fd(), which calls disk_force_media_change(lo->lo_disk).
  2. disk_force_media_change() first calls disk_event_uevent(disk, DISK_EVENT_MEDIA_CHANGE). Inside kobject_uevent_env(), block_uevent() is executed and reads disk->diskseq, capturing the old sequence number (e.g., 0x99b).
  3. The netlink uevent announcing DISK_EVENT_MEDIA_CHANGE is transmitted with DISKSEQ=0x99b.
  4. Task 2978 (CPU 1, udevd): Upon receiving the media change uevent, udevd immediately reads the device's sysfs /sys/devices/.../uevent attribute to refresh device information. Handling sysfs_kf_seq_show() -> uevent_show() -> dev_uevent() -> block_uevent() causes CPU 1 to read disk->diskseq.
  5. Simultaneously, CPU 0 returns from disk_event_uevent() and executes line 299: inc_diskseq(disk). This performs an unsynchronized 8-byte write to disk->diskseq (0x99b -> 0x99c).

Because disk_event_uevent() signals user space before updating diskseq, user space is deterministically woken up to query sysfs concurrently with the write in inc_diskseq(). Additionally, accesses to disk->diskseq are unprotected plain loads/stores without atomic/concurrency accessors (e.g., READ_ONCE/WRITE_ONCE), leading to the reported KCSAN data race.

▶ ▼ ✨ AI Jobs (2)
ID Workflow Result Correct Bug Created Started Finished Revision
a67980a0 assessment-security DenialOfService: ❌ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KCSAN: data-race in block_uevent / inc_diskseq (3) 2026/11/11 17:08 2026/11/11 17:08 2026/11/11 17:51 8d68b303
721c8cb5 assessment-kcsan Benign: ✅ ❓ KCSAN: data-race in block_uevent / inc_diskseq (3) 2026/11/11 17:08 2026/11/11 17:08 2026/11/11 18:12 8d68b303
▶ ▼ Similar bugs (2)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
upstream KCSAN: data-race in block_uevent / inc_diskseq block 6 37 972d 1068d 0/29 auto-obsoleted due to no activity on 2024/04/18 10:08
upstream KCSAN: data-race in block_uevent / inc_diskseq (2) race:harmful prio:low block 6 185 87d 875d 0/29 auto-obsoleted due to no activity on 2026/10/11 15:59

Sample crash report:
==================================================================
BUG: KCSAN: data-race in block_uevent / inc_diskseq

write to 0xffff88810252d610 of 8 bytes by task 659 on cpu 0:
 inc_diskseq+0x2d/0x40 block/genhd.c:1595
 disk_force_media_change+0x9e/0xe0 block/disk-events.c:299
 __loop_clr_fd drivers/block/loop.c:1073 [inline]
 lo_release+0x2e1/0x3f0 drivers/block/loop.c:1673
 bdev_release+0x375/0x3d0 block/bdev.c:-1
 blkdev_release+0x15/0x20 block/fops.c:690
 __fput+0x286/0x610 fs/file_table.c:512
 fput_close_sync+0xad/0x190 fs/file_table.c:618
 filp_close_sync fs/open.c:1574 [inline]
 __do_sys_close fs/open.c:1597 [inline]
 __se_sys_close fs/open.c:1584 [inline]
 __x64_sys_close+0x55/0xf0 fs/open.c:1584
 x64_sys_call+0x812/0x2550 arch/x86/include/generated/asm/syscalls_64.h:4
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x116/0x380 arch/x86/entry/syscall_64.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

read to 0xffff88810252d610 of 8 bytes by task 2978 on cpu 1:
 block_uevent+0x31/0x50 block/genhd.c:1328
 dev_uevent+0x336/0x3c0 drivers/base/core.c:2774
 uevent_show+0x11a/0x200 drivers/base/core.c:2830
 dev_attr_show+0x3f/0xa0 drivers/base/core.c:2491
 sysfs_kf_seq_show+0x1a3/0x280 fs/sysfs/file.c:65
 kernfs_seq_show+0x82/0xa0 fs/kernfs/file.c:216
 seq_read_iter+0x2d9/0x8f0 fs/seq_file.c:231
 kernfs_fop_read_iter+0xbe/0x320 fs/kernfs/file.c:290
 new_sync_read fs/read_write.c:473 [inline]
 vfs_read+0x699/0x7c0 fs/read_write.c:554
 ksys_read+0xdc/0x1a0 fs/read_write.c:696
 __do_sys_read fs/read_write.c:705 [inline]
 __se_sys_read fs/read_write.c:703 [inline]
 __x64_sys_read+0x40/0x50 fs/read_write.c:703
 x64_sys_call+0x2135/0x2550 arch/x86/include/generated/asm/syscalls_64.h:1
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x116/0x380 arch/x86/entry/syscall_64.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

value changed: 0x000000000000099b -> 0x000000000000099c

Reported by Kernel Concurrency Sanitizer on:
CPU: 1 UID: 0 PID: 2978 Comm: udevd Tainted: G        W           syzkaller #0 PREEMPT(lazy) 
Tainted: [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
==================================================================

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/11 17:08 upstream 067c5c24cef2 e56a968c .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-kcsan-gce KCSAN: data-race in block_uevent / inc_diskseq
* Struck through repros no longer work on HEAD.