From nobody Thu Apr 9 04:03:27 2026 Received: from m16.mail.163.com (m16.mail.163.com [220.197.31.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 55BAB3B388C; Wed, 11 Mar 2026 07:49:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.2 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773215374; cv=none; b=MAmVZR+rkm9vQQ5cT5KdbetRhj6NrjK/iEqnuexavBDEjIPCk7K394Yu3ZieEMFXR8CZSdhjUali1LAuKlt0nXni3ottkaMp8PP4Bi6Jr/H43mMHbkzbE47whbHP2ZYfvhPYkarWHg9fDEnurg2didBupATT3gc2PA7TEALFk0M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773215374; c=relaxed/simple; bh=27Uo0MWuXB9MbIMGw/HEhHvxwdK74yazPnu0pRmJw3A=; h=Date:From:To:Subject:Content-Type:MIME-Version:Message-ID; b=P6nFFlxSsRVk7xcsRYYFl/T9oti3Z39hvHVQt8wNNXSEYHl9Rfc8vlUvdIy2SIzcgFVSO/lHSrIfdyBQTZnP//KqctVWvA6R1DoO0a7NN4qWwQLlQyelo0a64TCqMwj3mft8cEx8F2f9MuD3O0RyGINPbaBk/izZ3MMBs+NMEc8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=QoTN+IqL; arc=none smtp.client-ip=220.197.31.2 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="QoTN+IqL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=Date:From:To:Subject:Content-Type:MIME-Version: Message-ID; bh=27Uo0MWuXB9MbIMGw/HEhHvxwdK74yazPnu0pRmJw3A=; b=Q oTN+IqLaRv4Cry6lA8KxyrjThRp7nmzlV0CtIZsK2oFTLCuFp8ahcnXGsh50Mru3 2ENbZxwyc5qTpbmBE4/TAy4nIrWhBB2/ao09EMNtbpFeRkAowLool+5w5ZltY4xh AhEd2WSLxZZU+OXRpciTaMBWAHX9RDbwmPL/QxLIz8= Received: from luckd0g$163.com ( [183.205.138.18] ) by ajax-webmail-wmsvr-40-127 (Coremail) ; Wed, 11 Mar 2026 15:49:05 +0800 (CST) Date: Wed, 11 Mar 2026 15:49:05 +0800 (CST) From: "Jianzhou Zhao" To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, jack@suse.cz, brauner@kernel.org, viro@zeniv.linux.org.uk Subject: KCSAN: data-race in __remove_assoc_queue / mark_buffer_dirty_inode X-Priority: 3 X-Mailer: Coremail Webmail Server Version 2023.4-cmXT build 20251222(83accb85) Copyright (c) 2002-2026 www.mailtech.cn 163com X-NTES-SC: AL_Qu2cAf6auksv4yKQbOkfmU4Rhug7UMO3uf8n24JfPJ9wjA/p2yseUUF9NmPf88CwFTuXvxiGfTNO1/ZAU5BifrwxF3q3byd9Mp6ioSs2vcJEPA== Content-Transfer-Encoding: quoted-printable Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <7852a82.6830.19cdbdeea92.Coremail.luckd0g@163.com> X-Coremail-Locale: zh_CN X-CM-TRANSID: fygvCgCXFblxHrFpBb12AA--.38874W X-CM-SenderInfo: poxfyvkqj6il2tof0z/xtbC9hFq9GmxHnGuKwAA3L X-Coremail-Antispam: 1U5529EdanIXcx71UUUUU7vcSsGvfC2KfnxnUU== Content-Type: text/plain; charset="utf-8" Subject: [BUG] fs/buffer: KCSAN: data-race in __remove_assoc_queue / mark_b= uffer_dirty_inode Dear Maintainers, We are writing to report a KCSAN-detected data race vulnerability within `f= s/buffer.c`. This bug was found by our custom fuzzing tool, RacePilot. The = race condition occurs when `__remove_assoc_queue` updates `bh->b_assoc_map`= while `mark_buffer_dirty_inode` performs a lockless speculative read on th= e exact same variable before trying to acquire the associative lock. We obs= erved this bug on the Linux kernel version 6.18.0-08691-g2061f18ad76e-dirty. Call Trace & Context =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D BUG: KCSAN: data-race in __remove_assoc_queue / mark_buffer_dirty_inode write to 0xffff88802a6cc1f8 of 8 bytes by task 25093 on cpu 1: __remove_assoc_queue+0xae/0xd0 fs/buffer.c:524 fsync_buffers_list+0x183/0x750 fs/buffer.c:823 sync_mapping_buffers+0x59/0x90 fs/buffer.c:585 fat_file_fsync+0xbb/0x100 fs/fat/file.c:195 vfs_fsync_range+0xe8/0x170 fs/sync.c:197 generic_write_sync include/linux/fs.h:2630 [inline] generic_file_write_iter+0x1ee/0x210 mm/filemap.c:4494 new_sync_write fs/read_write.c:605 [inline] vfs_write+0x78f/0x910 fs/read_write.c:701 ksys_write+0xbe/0x190 fs/read_write.c:753 __do_sys_write fs/read_write.c:764 [inline] __se_sys_write fs/read_write.c:761 [inline] __x64_sys_write+0x41/0x50 fs/read_write.c:761 x64_sys_call+0x1022/0x2030 arch/x86/include/generated/asm/syscalls_64.h:2 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xae/0x2c0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f read to 0xffff88802a6cc1f8 of 8 bytes by task 25074 on cpu 0: mark_buffer_dirty_inode+0x9c/0x250 fs/buffer.c:711 fat_mirror_bhs+0x280/0x3b0 fs/fat/fatent.c:417 fat_alloc_clusters+0xaed/0xb90 fs/fat/fatent.c:568 fat_add_cluster+0x34/0xc0 fs/fat/inode.c:111 __fat_get_block fs/fat/inode.c:159 [inline] fat_get_block+0x3c4/0x550 fs/fat/inode.c:194 __block_write_begin_int+0x29e/0xcd0 fs/buffer.c:2186 block_write_begin+0x74/0xf0 fs/buffer.c:2297 cont_write_begin+0x402/0x5d0 fs/buffer.c:2635 fat_write_begin+0x4f/0xe0 fs/fat/inode.c:233 generic_perform_write+0x13c/0x4c0 mm/filemap.c:4341 __generic_file_write_iter+0x117/0x130 mm/filemap.c:4464 generic_file_write_iter+0xa5/0x210 mm/filemap.c:4490 new_sync_write fs/read_write.c:605 [inline] vfs_write+0x78f/0x910 fs/read_write.c:701 ksys_write+0xbe/0x190 fs/read_write.c:753 __do_sys_write fs/read_write.c:764 [inline] __se_sys_write fs/read_write.c:761 [inline] __x64_sys_write+0x41/0x50 fs/read_write.c:761 x64_sys_call+0x1022/0x2030 arch/x86/include/generated/asm/syscalls_64.h:2 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xae/0x2c0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f value changed: 0xffff88802a5fa008 -> 0x0000000000000000 Reported by Kernel Concurrency Sanitizer on: CPU: 0 UID: 0 PID: 25074 Comm: syz.2.1198 Not tainted 6.18.0-08691-g2061f18= ad76e-dirty #44 PREEMPT(voluntary)=20 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/= 2014 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Execution Flow & Code Context During a buffer fsync trigger (e.g. from `fat_file_fsync`), `fsync_buffers_= list()` is responsible for looping over the inode's private buffer list. At= the start of its loop, it isolates the buffer and calls `__remove_assoc_qu= eue()`, which clears `bh->b_assoc_map` using a plain C store under `buffer_= mapping->i_private_lock`: ```c // fs/buffer.c static void __remove_assoc_queue(struct buffer_head *bh) { list_del_init(&bh->b_assoc_buffers); WARN_ON(!bh->b_assoc_map); bh->b_assoc_map =3D NULL; // <-- Plain concurrent write } ``` Meanwhile, another process actively dirtying a buffer triggers `mark_buffer= _dirty_inode()`. This function optimistically checks whether the buffer hea= d is already associated with an inode mapping structure using a lockless pe= ek at `bh->b_assoc_map`. If the map is unassigned, it acquires the target `= i_private_lock` and updates it: ```c // fs/buffer.c void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode) { ... if (!bh->b_assoc_map) { // <-- Lockless plain concurrent read spin_lock(&buffer_mapping->i_private_lock); list_move_tail(&bh->b_assoc_buffers, &mapping->i_private_list); bh->b_assoc_map =3D mapping; spin_unlock(&buffer_mapping->i_private_lock); } } ``` Root Cause Analysis A read-write KCSAN data race arises because `__remove_assoc_queue()` assign= s `bh->b_assoc_map` while holding `i_private_lock` without employing any me= mory model volatile annotations. At the exact same snapshot, `mark_buffer_d= irty_inode()` evaluates `if (!bh->b_assoc_map)` out of the lock domain to o= ptimize out taking the spinlock for an already-associated buffer block.=20 Unfortunately, we were unable to generate a reproducer for this bug. Potential Impact This data race is largely benign from a runtime control-flow perspective; t= he lack of association triggers a spinlock wait to assign the buffer, where= as an obsolete read evaluating to actual pointers skips the lock and skips = the queue placement. If extreme read tearing or heavy compiler optimization= happens, it could possibly lead to duplicate list inclusions or desynchron= ised buffer dirty associations, which could eventually yield missed buffers= during `fsync` requests. However, triggering KCSAN logs adds extensive noi= se over expected logic. Proposed Fix To align with the Linux Memory Model and inform KCSAN that this speculative= read is intentional and expected (and to prevent compiler tearing optimisa= tions), we should simply wrap the condition check in `mark_buffer_dirty_ino= de` using the `data_race()` macro. Furthermore, employing `WRITE_ONCE` in `= __remove_assoc_queue` reinforces safety.=20 ```diff --- a/fs/buffer.c +++ b/fs/buffer.c @@ -522,7 +522,7 @@ static void __remove_assoc_queue(struct buffer_head *bh) { list_del_init(&bh->b_assoc_buffers); WARN_ON(!bh->b_assoc_map); - bh->b_assoc_map =3D NULL; + WRITE_ONCE(bh->b_assoc_map, NULL); } =20 int inode_has_buffers(struct inode *inode) @@ -712,7 +712,7 @@ void mark_buffer_dirty_inode(struct buffer_head *bh, st= ruct inode *inode) } else { BUG_ON(mapping->i_private_data !=3D buffer_mapping); } - if (!bh->b_assoc_map) { + if (!data_race(bh->b_assoc_map)) { spin_lock(&buffer_mapping->i_private_lock); list_move_tail(&bh->b_assoc_buffers, &mapping->i_private_list); bh->b_assoc_map =3D mapping; ``` We would be highly honored if this could be of any help. Best regards, RacePilot Team