From: "Jianzhou Zhao" <luckd0g@163.com>
To: jack@suse.cz, viro@zeniv.linux.org.uk, brauner@kernel.org,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: KCSAN: data-race in path_lookupat / vfs_rename
Date: Wed, 11 Mar 2026 10:54:28 +0800 (CST) [thread overview]
Message-ID: <61ff6def.2d0d.19cdad1307e.Coremail.luckd0g@163.com> (raw)
Subject: [BUG] fs/namei: KCSAN: data-race in path_lookupat / vfs_rename (`d_flags`)
Dear VFS Maintainers,
Our fuzzing tool, RacePilot, has detected a data race in the VFS subsystem on a 6.18 kernel version (`6.18.0-08691-g2061f18ad76e-dirty`).
The race occurs between `vfs_rename()` modifying a victim dentry's `d_flags` via `dont_mount()` and a concurrent path lookup (`path_lookupat()`) executing lockless reads on the same `d_flags` via `d_managed()`.
### Call Trace & Context
```
BUG: KCSAN: data-race in path_lookupat / vfs_rename
write to 0xffff88802d62b180 of 4 bytes by task 4204 on cpu 0:
dont_mount include/linux/dcache.h:391 [inline]
vfs_rename+0xf03/0x13a0 fs/namei.c:5963
do_renameat2+0x6a9/0x8e0 fs/namei.c:6072
...
read to 0xffff88802d62b180 of 4 bytes by task 4691 on cpu 1:
d_managed include/linux/dcache.h:412 [inline]
step_into fs/namei.c:2102 [inline]
walk_component fs/namei.c:2258 [inline]
lookup_last fs/namei.c:2753 [inline]
path_lookupat+0x1fc/0x740 fs/namei.c:2777
...
value changed: 0x00300080 -> 0x00004180
```
#### Execution Flow & Code Context:
**1. The Write Path (`vfs_rename` -> `dont_mount`)**
During a rename operation, if a target (victim) dentry is going to be replaced, `vfs_rename` unmounts any mounts attached to it:
```c
5978: if (!(flags & RENAME_EXCHANGE) && target) {
5979: if (is_dir) {
5980: shrink_dcache_parent(new_dentry);
5981: target->i_flags |= S_DEAD;
5982: }
5983: dont_mount(new_dentry); // <--- Invokes dont_mount on the victim
5984: detach_mounts(new_dentry);
5985: }
```
Inside `dont_mount(struct dentry *dentry)` (`include/linux/dcache.h`), `d_flags` is updated while holding the `d_lock`:
```c
388: static inline void dont_mount(struct dentry *dentry)
389: {
390: spin_lock(&dentry->d_lock);
391: dentry->d_flags |= DCACHE_CANT_MOUNT; // <--- Read-Modify-Write
392: spin_unlock(&dentry->d_lock);
393: }
```
**2. The Read Path (`path_lookupat` -> `step_into` -> `d_managed`)**
Concurrently, a separate thread traversing the filesystem performs a path lookup and inspects the same intermediate dentry (the rename victim) within `step_into()`. It tests if the dentry needs mount-point translation by calling `d_managed()`:
```c
412: static inline bool d_managed(const struct dentry *dentry)
413: {
414: return dentry->d_flags & DCACHE_MANAGED_DENTRY; // <--- Lockless Read
415: }
```
### Root Cause Analysis
The data race is triggered because `d_managed()` executes a plain read of `dentry->d_flags` without holding `d_lock` (or using `READ_ONCE`), while `dont_mount()` concurrently modifies `dentry->d_flags` using a plain read-modify-write operation (`|= DCACHE_CANT_MOUNT`).
Although `dont_mount()` properly protects the write within a `spin_lock`, the lockless reader is oblivious to it. KCSAN identifies this as a data race because the plain read in `d_managed()` can overlap with the unlocked portion of the compiler's emitted store sequence in another CPU.
Regrettably, we were unable to create a reproduction program for this bug.
### Potential Impact
While a torn read of a 32-bit aligned integer is highly unlikely on most modern architectures, plain concurrent access constitutes undefined behavior under the C memory model. The compiler might apply unforeseen transformations capable of producing a stale or transient state representation. However, pragmatically speaking, since the victim dentry is concurrently being purged/renamed, misinterpreting the managed flags would likely lead to subsequent validation failures upstream within namei.c, causing the traversal to bounce to slow paths safely. Therefore, we evaluate the severity of this issue as a minor data race that primarily risks undefined behavior and triggers KCSAN warnings.
### Proposed Fix
To silence the KCSAN warning and adhere securely to kernel concurrency practices, the lockless read of `d_flags` in `d_managed()` should be explicitly annotated using `data_race()` or, more robustly, `READ_ONCE()`. Using `READ_ONCE()` guarantees single-instruction load semantics preventing compiler optimizations that might induce tearing.
```diff
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -411,7 +411,7 @@ extern void dput(struct dentry *);
static inline bool d_managed(const struct dentry *dentry)
{
- return dentry->d_flags & DCACHE_MANAGED_DENTRY;
+ return READ_ONCE(dentry->d_flags) & DCACHE_MANAGED_DENTRY;
}
```
(Alternatively, using `data_race()` if no load-tearing dangers exist in theory on the supported architectures, depending on VFS lockless scaling conventions for d_flags).
We would be highly honored if this could be of any help.
Best regards,
[Your Name/Team]
RacePilot Team
next prev reply other threads:[~2026-03-11 2:54 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-11 8:02 KCSAN: data-race in __d_drop / retain_dentry Jianzhou Zhao
2026-03-11 7:55 ` KCSAN: data-race in step_into_slowpath / vfs_unlink Jianzhou Zhao
2026-03-11 7:49 ` KCSAN: data-race in __remove_assoc_queue / mark_buffer_dirty_inode Jianzhou Zhao
2026-03-11 2:54 ` Jianzhou Zhao [this message]
2026-03-11 10:38 ` KCSAN: data-race in __d_drop / retain_dentry Christian Brauner
2026-03-17 12:37 ` KCSAN: data-race in __remove_assoc_queue / mark_buffer_dirty_inode Jan Kara
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=61ff6def.2d0d.19cdad1307e.Coremail.luckd0g@163.com \
--to=luckd0g@163.com \
--cc=brauner@kernel.org \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox