KCSAN: data-race in fat32_ent_put / fat_mirror_bhs (5)

Status: moderation: reported on 2026/10/29 09:15
Subsystems: exfat
Labels: prio:low race:benign
[Documentation on labels]
Reported-by: syzbot+bdd6980049e6678b3b55@syzkaller.appspotmail.com
First crash: 12h05m, last: 12h05m
✨ AI-generated root cause summary:
A data race occurs between concurrent file writes on the same FAT filesystem because operations on FAT table blocks lack proper synchronization via sbi->fat_lock. In __fat_alloc_clusters(), unlock_fat() is called prematurely before fat_mirror_bhs() copies the primary FAT buffer to the backup FAT with memcpy(), while concurrently fat_ent_write() executes ops->ent_put() without acquiring sbi->fat_lock at all. In production kernels, this race results in benign or temporary metadata inconsistency between the primary and mirror FAT tables without memory corruption or system crashes.
▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit c339f4010dea:
### Technical Root Cause Analysis #### 1. Functions, Structs, and Files Involved - **Files:** - `fs/fat/fatent.c` - **Functions:** - `fat32_ent_put()` / `ops->ent_put()` (`fs/fat/fatent.c:190`) - `fat_ent_write()` (`fs/fat/fatent.c:415`) - `fat_mirror_bhs()` (`fs/fat/fatent.c:380`) - `__fat_alloc_clusters()` (`fs/fat/fatent.c:506`) - `lock_fat()` / `unlock_fat()` (`fs/fat/fatent.c:283, 288`) - **Structs and Fields:** - `struct msdos_sb_info` (`fs/fat/fat.h`): field `fat_lock` (mutex protecting FAT entry manipulations) - `struct fat_entry`: fields `u.ent32_p` and `bhs[]` (holding pointers to the FAT table buffer heads) - `struct buffer_head`: field `b_data` (holding cached FAT block data) --- #### 2. Trigger Mechanism and Violated Invariants Even though file write paths serialize operations per-inode using `inode_lock(inode)` (e.g., in `generic_file_write_iter()`), two concurrent tasks operating on *different* files belonging to the same FAT filesystem operate under separate inode locks. Both tasks, however, share the filesystem-wide File Allocation Table (FAT) and its underlying buffer heads (`bhs[n]->b_data`). In FAT filesystem internals, access to the FAT entries and the mirroring of dirty FAT blocks to the backup FAT copies are supposed to be serialized via `sbi->fat_lock` (accessed via `lock_fat(sbi)` and `unlock_fat(sbi)`): 1. **Premature unlock in cluster allocation (`__fat_alloc_clusters`):** In `fs/fat/fatent.c`, `__fat_alloc_clusters()` acquires `lock_fat(sbi)` while modifying FAT entries. However, at line 585 it calls `unlock_fat(sbi)` *before* synchronizing and mirroring the dirty FAT blocks to the backup FAT table (`fat_mirror_bhs(sb, bhs, nr_bhs)` at line 592). This is in contrast to `fat_free_clusters()`, which keeps `lock_fat(sbi)` held throughout `fat_mirror_bhs()`. 2. **Missing `lock_fat()` synchronization in `fat_ent_write`:** `fat_ent_write()` is called when writing an EOF or appending clusters (such as from `fat_free()` during truncate or `fat_chain_add()`). `fat_ent_write()` directly calls `ops->ent_put(fatent, new)` and `fat_mirror_bhs()` without acquiring `lock_fat(sbi)` at all. 3. **The Race:** - Task 6394 in `__fat_alloc_clusters()` drops `lock_fat(sbi)` and calls `fat_mirror_bhs()`, which iterates over `bhs` and runs `memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize)` on CPU 1 (reading 2048 bytes at `0xffff88811d9e5000`). - Simultaneously, Task 6401 on CPU 0 is handling a truncate on another file (`fat_truncate_blocks()` -> `fat_free()` -> `fat_ent_write()`), calling `fat32_ent_put()`, which writes 4 bytes to `*fatent->u.ent32_p` at address `0xffff88811d9e53f0` without holding `lock_fat()`. - Because both tasks access the exact same primary FAT buffer concurrently without mutual exclusion, KCSAN detects the read-write data race between `memcpy()` and `*fatent->u.ent32_p = ...`. --- #### 3. Production Runtime Consequences and Classification - **Memory Corruption (`MemoryCorruption = false`):** The race is an unsynchronized read/write to filesystem block buffers (`b_data`). No kernel objects, linked list pointers, allocation sizes, refcounts, or kernel control structures are overwritten or corrupted. The values manipulated are cluster index integers on buffer-head pages. Thus, no kernel memory corruption, use-after-free, or out-of-bounds write occurs. - **Information Leak (`InfoLeak = "none"`):** The race does not expose kernel memory contents or kernel pointer addresses to userspace. - **Denial of Service (`RawDenialOfService = false`):** In a production kernel without sanitizers and with `panic_on_warn` disabled: - On architectures like x86_64, aligned 4-byte writes are atomic; `memcpy()` in `fat_mirror_bhs()` reads either the pre-update or post-update value into the backup FAT copy. - Furthermore, `fat_ent_write()` immediately proceeds to call `fat_mirror_bhs()` for its own update. - The primary in-memory FAT buffer remains consistent, and FAT cluster chain traversals in the kernel validate cluster limits and have explicit cycle detection (`fat_fs_error_ratelimit`), preventing infinite loops or kernel deadlocks/panics. - System execution proceeds normally without kernel panics, crashes, hangs, or resource leaks. - **Candidates (`IsNetCandidate = false`, `IsKVMCandidate = false`):** The bug is entirely within the local FAT filesystem implementation and does not involve networking or virtualization/KVM subsystem code.
▶ ▼ ✨ AI Jobs (2)
ID Workflow Result Correct Bug Created Started Finished Revision
0d11bb61 assessment-security DenialOfService: ❌ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KCSAN: data-race in fat32_ent_put / fat_mirror_bhs (5) 2026/10/29 09:14 2026/10/29 09:15 2026/10/29 09:46 b1d00f76
75f3f998 assessment-kcsan Benign: ✅ ❓ KCSAN: data-race in fat32_ent_put / fat_mirror_bhs (5) 2026/10/29 09:14 2026/10/29 09:14 2026/10/29 10:21 b1d00f76
▶ ▼ Similar bugs (4)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
upstream KCSAN: data-race in fat32_ent_put / fat_mirror_bhs (3) prio:low exfat 6 5 161d 263d 0/29 auto-obsoleted due to no activity on 2026/07/16 08:52
upstream KCSAN: data-race in fat32_ent_put / fat_mirror_bhs (2) exfat 6 1 359d 359d 0/29 auto-obsoleted due to no activity on 2025/12/30 19:51
upstream KCSAN: data-race in fat32_ent_put / fat_mirror_bhs exfat 6 1 491d 491d 0/29 auto-obsoleted due to no activity on 2025/08/20 03:32
upstream KCSAN: data-race in fat32_ent_put / fat_mirror_bhs (4) race:harmful prio:low exfat 6 1 77d 77d 0/29 auto-obsoleted due to no activity on 2026/10/07 23:32

Sample crash report:
==================================================================
BUG: KCSAN: data-race in fat32_ent_put / fat_mirror_bhs

read-write to 0xffff88811d9e53f0 of 4 bytes by task 6401 on cpu 0:
 fat32_ent_put+0x4e/0x90 fs/fat/fatent.c:194
 fat_ent_write+0x76/0x160 fs/fat/fatent.c:422
 fat_free fs/fat/file.c:364 [inline]
 fat_truncate_blocks+0x37a/0x5b0 fs/fat/file.c:396
 fat_write_failed fs/fat/inode.c:220 [inline]
 fat_write_end+0xba/0x160 fs/fat/inode.c:248
 generic_perform_write+0x314/0x490 mm/filemap.c:4413
 __generic_file_write_iter+0xec/0x120 mm/filemap.c:4506
 generic_file_write_iter+0x77/0x1f0 mm/filemap.c:4535
 do_iter_readv_writev+0x4f6/0x5a0 fs/read_write.c:-1
 vfs_writev+0x2ce/0x8b0 fs/read_write.c:1058
 do_pwritev fs/read_write.c:1154 [inline]
 __do_sys_pwritev2 fs/read_write.c:1212 [inline]
 __se_sys_pwritev2+0xfc/0x1c0 fs/read_write.c:1203
 __x64_sys_pwritev2+0x67/0x80 fs/read_write.c:1203
 x64_sys_call+0x899/0x2550 arch/x86/include/generated/asm/syscalls_64.h:329
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x112/0x360 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

read to 0xffff88811d9e5000 of 2048 bytes by task 6394 on cpu 1:
 fat_mirror_bhs+0x1da/0x320 fs/fat/fatent.c:399
 __fat_alloc_clusters+0xc5c/0xd80 fs/fat/fatent.c:592
 fat_alloc_chain+0x25/0x30 fs/fat/fatent.c:610
 fat_add_clusters fs/fat/inode.c:109 [inline]
 __fat_get_block fs/fat/inode.c:156 [inline]
 fat_get_block+0x252/0x5e0 fs/fat/inode.c:191
 __block_write_begin_int+0x421/0x1060 fs/buffer.c:2031
 block_write_begin fs/buffer.c:2145 [inline]
 cont_write_begin+0x5bf/0x920 fs/buffer.c:2507
 fat_write_begin+0x52/0xe0 fs/fat/inode.c:231
 cont_expand_zero fs/buffer.c:2435 [inline]
 cont_write_begin+0x18d/0x920 fs/buffer.c:2497
 fat_write_begin+0x52/0xe0 fs/fat/inode.c:231
 generic_perform_write+0x186/0x490 mm/filemap.c:4392
 __generic_file_write_iter+0x9e/0x120 mm/filemap.c:4509
 generic_file_write_iter+0x77/0x1f0 mm/filemap.c:4535
 new_sync_write fs/read_write.c:595 [inline]
 vfs_write+0x57a/0x990 fs/read_write.c:687
 ksys_pwrite64 fs/read_write.c:794 [inline]
 __do_sys_pwrite64 fs/read_write.c:802 [inline]
 __se_sys_pwrite64 fs/read_write.c:799 [inline]
 __x64_sys_pwrite64+0xfd/0x150 fs/read_write.c:799
 x64_sys_call+0x23be/0x2550 arch/x86/include/generated/asm/syscalls_64.h:19
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x112/0x360 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Reported by Kernel Concurrency Sanitizer on:
CPU: 1 UID: 0 PID: 6394 Comm: syz.4.936 Not tainted syzkaller #0 PREEMPT(lazy) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/07/2026
==================================================================

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/10/29 09:14 upstream c339f4010dea b1d00f76 .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-kcsan-gce KCSAN: data-race in fat32_ent_put / fat_mirror_bhs
* Struck through repros no longer work on HEAD.