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: 1d15h, last: 1d15h
✨ 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:
  • Functions:
  • 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 162d 264d 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 360d 360d 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 492d 492d 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 79d 79d 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.