syzbot
KCSAN: data-race in _copy_to_iter / fat12_ent_put (9)
Status:
moderation: reported on 2026/11/01 22:47
Subsystems:
exfat
Labels:
prio:low
race:benign
[Documentation on labels]
Reported-by: syzbot+bd0378ab4a6217b84474@syzkaller.appspotmail.com
First crash: 15h40m, last: 15h36m
✨ AI-generated root cause summary:
A data race occurs between an in-place FAT12 metadata update and a concurrent read of the underlying block device from userspace. In fs/fat/fatent.c, fat12_ent_put() modifies a 12-bit FAT table entry in-place inside fatent->bhs[0]->b_data. Because sb_bread() buffers are backed by folios in the block device's page cache (sb->s_bdev->bd_inode->i_mapping), this memory is shared with direct block device reads. Concurrently, a userspace process (such as udevd) reading directly from the block device node invokes blkdev_read_iter() -> filemap_read(), which copies the folio to userspace via _copy_to_iter() without holding a lock on the folio, leading to the KCSAN data-race report.
▶
▼
✨ Full AI root cause analysis
Technical Root Cause Analysis
1. Components and Functions Involved
- Filesystem metadata writer (
fs/fat/fatent.c, fs/fat/misc.c, fs/fat/file.c):
fat_fallocate() / fat_cont_expand() / fat_chain_add() / fat_ent_write()
fat12_ent_put(struct fat_entry *fatent, int new): Updates a 12-bit entry in the FAT table in-place via pointer fatent->u.ent12_p[0], which points into fatent->bhs[0]->b_data.
fat12_entry_lock: Internal spinlock guarding non-byte-aligned FAT12 entry bit modifications across FAT internal operations.
- Block device reader (
block/fops.c, mm/filemap.c, lib/iov_iter.c):
blkdev_read_iter(): Called when a userspace process (here, udevd) performs a buffered read() directly on the block device node (e.g., /dev/sdX or /dev/loopX).
filemap_read(): Reads the cached folio from the block device's address space (bd_inode->i_mapping).
copy_folio_to_iter() -> _copy_to_iter() -> copy_to_user_iter(): Copies data from the kernel pagecache folio into userspace.
2. Detailed Mechanism and Trigger
-
Shared Buffer Storage:
When the FAT filesystem reads or modifies metadata sectors (the File Allocation Table), it calls sb_bread(sb, blocknr). In Linux, sb_bread() allocates buffer heads (struct buffer_head) backed by pages/folios in the block device's address space (sb->s_bdev->bd_inode->i_mapping). Thus, fatent->bhs[0]->b_data points directly into the memory of a pagecache folio belonging to the block device.
-
In-place Filesystem Update (Writer):
When extending a file (fallocate -> fat_cont_expand), FAT needs to link a new cluster into the cluster chain via fat_chain_add() -> fat_ent_write() -> ops->ent_put().
For FAT12, fat12_ent_put() acquires fat12_entry_lock and updates the entry directly in memory:
*ent12_p[0] = new & 0xff;
This writes 1 byte into the buffer head's data at byte offset 0x94.
-
Concurrent Raw Block Device Read (Reader):
Concurrently, a userspace system daemon (udevd) opens the underlying block device directly and issues a read() (e.g. probing partition tables, filesystem signatures, or volume labels).
blkdev_read_iter() takes inode_lock_shared(bd_inode) and calls filemap_read(). In filemap_read(), folios matching the requested file range are obtained from bd_inode->i_mapping.
filemap_read() calls copy_folio_to_iter(), which reads the 512-byte sector at 0xffff888124d81000 into userspace via _copy_to_iter().
-
Synchronization Gap and KCSAN Detection:
Pagecache reads in filemap_read() intentionally do not hold the folio lock across copy_folio_to_iter() to prevent ABBA deadlocks should a user-provided buffer fault into the same address space. Furthermore, blkdev_read_iter() only synchronizes against raw block device pagecache invalidations via inode_lock_shared(bd_inode).
Filesystem metadata modifications made via buffer heads (b_data) synchronize only with filesystem-internal locks (such as fat12_entry_lock), with no locking against direct, concurrent userspace reads on the raw block device node.
As a result, CPU 0 writes to 0xffff888124d81094 while CPU 1 reads [0xffff888124d81000, 0xffff888124d81200), triggering the KCSAN data-race warning.
Sample crash report:
* Struck through repros no longer work on HEAD.