Skip to contentSkip to conversation

BUG: KCSAN: data-race in ext4_fill_super / super_s_dev_test

DISCUSSION Jianzhou Zhao 1 participant 1 message Mar 11

lore
Share & tools
lore
#0 lore raw

Subject: [BUG] fs: KCSAN: data-race in ext4_fill_super / super_s_dev_test

Dear Maintainers,

We are writing to report a KCSAN-detected data race vulnerability within the VFS and ext4 subsystem. This bug was found by our custom fuzzing tool, RacePilot. The race occurs during the filesystem mount procedure when ext4_check_journal_data_mode (via ext4_fill_super) sets SB_I_CGROUPWB in sb->s_iflags without strict synchronization, while super_s_dev_test contemporaneously checks for the SB_I_RETIRED bit in the same s_iflags variable while iterating over fs_supers. We observed this bug on the Linux kernel version 6.18.0-08691-g2061f18ad76e-dirty.

Call Trace & Context
==================================================================

BUG: KCSAN: data-race in ext4_fill_super / super_s_dev_test

write to 0xffff88802f427058 of 8 bytes by task 25360 on cpu 1:

 ext4_check_journal_data_mode fs/ext4/super.c:5021 [inline]
 __ext4_fill_super fs/ext4/super.c:5364 [inline]
 ext4_fill_super+0x1308/0x7590 fs/ext4/super.c:5777
 get_tree_bdev_flags+0x256/0x370 fs/super.c:1699
 get_tree_bdev+0x1f/0x30 fs/super.c:1722
 ext4_get_tree+0x1c/0x30 fs/ext4/super.c:5809
 ...
 __x64_sys_mount+0x1d7/0x210 fs/namespace.c:4201

read to 0xffff88802f427058 of 8 bytes by task 25340 on cpu 0:

 super_s_dev_test+0x1c/0xa0 fs/super.c:1386
 sget_fc+0x13a/0x6c0 fs/super.c:755
 sget_dev fs/super.c:1413 [inline]
 get_tree_bdev_flags+0x124/0x370 fs/super.c:1685
 get_tree_bdev+0x1f/0x30 fs/super.c:1722
 ext4_get_tree+0x1c/0x30 fs/ext4/super.c:5809
 ...
 __x64_sys_mount+0x1d7/0x210 fs/namespace.c:4201

value changed: 0x0000000000000000 -> 0x0000000000004001

Reported by Kernel Concurrency Sanitizer on:

CPU: 0 UID: 0 PID: 25340 Comm: syz.1.1392 Not tainted 6.18.0-08691-g2061f18ad76e-dirty #44 PREEMPT(voluntary) 
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
==================================================================

Execution Flow & Code Context
During the mounting phase of a new ext4 instance, the system allocates a superblock and inserts it into the fs_supers list globally (handled in sget_fc()). Later, the ext4_fill_super() phase modifies sb->s_iflags configuration properties directly. This happens without acquiring the sb_lock which covers list traversals. Concretely, in fs/ext4/super.c:

```c
// fs/ext4/super.c
static int ext4_check_journal_data_mode(struct super_block *sb)
{
	...
	} else {
		sb->s_iflags |= SB_I_CGROUPWB; // <-- Plain concurrent write
	}
	return 0;
}
```

Whenever another concurrent mount thread (or internal iteration) searches through existing superblocks for the block device via sget_dev(), it iterates over the linked structures calling super_s_dev_test(). super_s_dev_test inspects s->s_iflags to verify the retired status.

```c
// fs/super.c
static int super_s_dev_test(struct super_block *s, struct fs_context *fc)
{
	return !(s->s_iflags & SB_I_RETIRED) && // <-- Plain concurrent read
		s->s_dev == *(dev_t *)fc->sget_key;
}
```

Root Cause Analysis
A KCSAN data race arises because one thread configures the internal superblock identifier flags concurrently while a secondary thread traverses the global list of superblocks scanning for active block devices. s->s_iflags acts as shared state globally visible through the filesystem subsystem list from the point it gets listed in sget_fc. The read evaluation of s_iflags overlaps with the logical OR operation, establishing an undocumented race. Unfortunately, we were unable to generate a reproducer for this bug.

Potential Impact
This data race is functionally extremely minor or mostly benign since the read only targets the SB_I_RETIRED bit, while the write sets the SB_I_CGROUPWB bit. However, the lack of annotations can trigger aggressive compiler tearing implementations across architecture layouts, risking transient false SB_I_RETIRED positives causing unexpected remount disconnections. It generates excessive fuzzing alerts that obscure critical concurrency flaws.

Proposed Fix
We can wrap the evaluation condition in super_s_dev_test() explicitly to advise KCSAN and suppress the diagnostic alert effectively, affirming the concurrent access is tracked by the developers and functionally verified.

```diff
--- a/fs/super.c
+++ b/fs/super.c
@@ -1383,7 +1383,7 @@ static int super_s_dev_set(struct super_block *s, struct fs_context *fc)
 
 static int super_s_dev_test(struct super_block *s, struct fs_context *fc)
 {
-	return !(s->s_iflags & SB_I_RETIRED) &&
+	return !(data_race(s->s_iflags) & SB_I_RETIRED) &&
 		s->s_dev == *(dev_t *)fc->sget_key;
 }
```

We would be highly honored if this could be of any help.

Best regards,
RacePilot Team