public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* KCSAN: data-race in __d_drop / retain_dentry
@ 2026-03-11  8:02 Jianzhou Zhao
  2026-03-11  7:55 ` KCSAN: data-race in step_into_slowpath / vfs_unlink Jianzhou Zhao
  2026-04-08 23:12 ` KCSAN: data-race in __d_drop / retain_dentry Al Viro
  0 siblings, 2 replies; 10+ messages in thread
From: Jianzhou Zhao @ 2026-03-11  8:02 UTC (permalink / raw)
  To: linux-kernel, viro, brauner, jack, linux-fsdevel



Subject: [BUG] fs: KCSAN: data-race in __d_drop / retain_dentry

Dear Maintainers,

We are writing to report a KCSAN-detected data race vulnerability within the VFS dcache subsystem (`fs/dcache.c` and `include/linux/list_bl.h`). This bug was found by our custom fuzzing tool, RacePilot. The race occurs when `__d_drop()` unhashes a dentry and maliciously clears `dentry->d_hash.pprev` concurrently against a lockless RCU reader executing `retain_dentry()` which calls `hlist_bl_unhashed()` to inspect `pprev`. We observed this bug on the Linux kernel version 6.18.0-08691-g2061f18ad76e-dirty.

Call Trace & Context
==================================================================
BUG: KCSAN: data-race in __d_drop / retain_dentry

write to 0xffff888013db3010 of 8 bytes by task 3021 on cpu 1:
 __d_drop fs/dcache.c:607 [inline]
 __d_drop+0x8c/0xd0 fs/dcache.c:601
 __dentry_kill+0xbd/0x3e0 fs/dcache.c:715
 dput fs/dcache.c:977 [inline]
 dput+0x123/0x220 fs/dcache.c:964
 handle_mounts fs/namei.c:1722 [inline]
 step_into_slowpath+0x688/0x960 fs/namei.c:2081
 ...
 __x64_sys_readlinkat+0x6f/0xa0 fs/stat.c:624

read to 0xffff888013db3010 of 8 bytes by task 4584 on cpu 0:
 hlist_bl_unhashed include/linux/list_bl.h:57 [inline]
 d_unhashed include/linux/dcache.h:374 [inline]
 retain_dentry+0x79/0x320 fs/dcache.c:809
 fast_dput fs/dcache.c:913 [inline]
 dput+0x97/0x220 fs/dcache.c:971
 end_dirop fs/namei.c:2939 [inline]
 do_unlinkat+0x332/0x540 fs/namei.c:5483
 ...
 __x64_sys_unlink+0x7d/0xa0 fs/namei.c:5513

value changed: 0xffff88807dbb55f8 -> 0x0000000000000000

Reported by Kernel Concurrency Sanitizer on:
CPU: 0 UID: 0 PID: 4584 Comm: systemd-udevd Not tainted 6.18.0-08691-g2061f18ad76e-dirty #50 PREEMPT(voluntary) 
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
==================================================================

Execution Flow & Code Context
When a dentry receives its final decrement during a path operation (e.g., inside `dput`), its lifecycle might traverse `__dentry_kill()` leading to `__d_drop()`. Here, VFS manually eradicates the dentry from the hash list by assigning `NULL` to the internal double-linked list pointer tracker `pprev`:
```c
// fs/dcache.c
void __d_drop(struct dentry *dentry)
{
	if (!d_unhashed(dentry)) {
		___d_drop(dentry);
		...
		dentry->d_hash.pprev = NULL; // <-- Plain concurrent write
		write_seqcount_invalidate(&dentry->d_seq);
	}
}
```

Simultaneously, another thread undergoing an optimistic lockless `dput` (e.g., `fast_dput` resolving symbolic links or unlinking) probes whether the unreferenced dentry should be retained via `retain_dentry()`. `retain_dentry` verifies `d_unhashed()` relying on `hlist_bl_unhashed()`:
```c
// include/linux/list_bl.h
static inline bool hlist_bl_unhashed(const struct hlist_bl_node *h)
{
	return !h->pprev; // <-- Plain concurrent read
}
```

Root Cause Analysis
A KCSAN data race materializes because `__d_drop` resets `dentry->d_hash.pprev` using standard assignments, while `retain_dentry` inspects `pprev` outside the dentry's lock protection. The Linux kernel explicitly permits optimistic RCU verification of `d_unhashed` within `retain_dentry` given that transient inaccuracies gracefully fall through to a strictly-locked verification slow-path (`locked: if (dentry->d_lockref.count || retain_dentry(dentry, true))`). This lockless access is an architectural optimization; however, reading and writing the 8-byte `pprev` pointer without safe `READ_ONCE()` and `WRITE_ONCE()` directives violates the Memory Model constraints under KCSAN and exposes potential tearing risks across compiler transformations. 
Unfortunately, we were unable to generate a reproducer for this bug.

Potential Impact
This data race technically threatens architectures or compilers prone to load/store tearing resulting in a garbled non-NULL `pprev` pointer value snapshot. Nonetheless, functionally, the impact is minuscule: since `retain_dentry` simply falls back to the properly synchronized path if it wrongly presumes the dentry is not unhashed, there is no direct vulnerability. The data race nevertheless generates false-positive diagnostic spam obscuring more pertinent memory corruption flaws.

Proposed Fix
To codify the lockless RCU access paradigm for `hlist_bl_unhashed` reliably across VFS and properly placate KCSAN, we apply `WRITE_ONCE` to the hash decoupling and `READ_ONCE` for the state examination.

```diff
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -604,7 +604,7 @@ void __d_drop(struct dentry *dentry)
 		___d_drop(dentry);
 		__sanitizer_obj_cov_trace_pc(268);
 		__sanitizer_obj_cov_trace_pc(436);
-		dentry->d_hash.pprev = NULL;
+		WRITE_ONCE(dentry->d_hash.pprev, NULL);
 		write_seqcount_invalidate(&dentry->d_seq);
 	}
 }
--- a/include/linux/list_bl.h
+++ b/include/linux/list_bl.h
@@ -54,7 +54,7 @@ static inline bool hlist_bl_unhashed(const struct hlist_bl_node *h)
 	__sanitizer_obj_cov_trace_pc(386);
 	__sanitizer_obj_cov_trace_pc(1106);
 	__sanitizer_obj_cov_trace_pc(1107);
-	return !h->pprev;
+	return !READ_ONCE(h->pprev);
 }
 
 static inline struct hlist_bl_node *hlist_bl_first(struct hlist_bl_head *h)
```

We would be highly honored if this could be of any help.

Best regards,
RacePilot Team

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-04-08 23:30 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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     ` KCSAN: data-race in path_lookupat / vfs_rename Jianzhou Zhao
2026-03-11 10:38       ` KCSAN: data-race in __d_drop / retain_dentry Christian Brauner
2026-04-08 23:04       ` KCSAN: data-race in path_lookupat / vfs_rename Al Viro
2026-04-08 23:06         ` Al Viro
2026-03-17 12:37     ` KCSAN: data-race in __remove_assoc_queue / mark_buffer_dirty_inode Jan Kara
2026-04-08 23:12 ` KCSAN: data-race in __d_drop / retain_dentry Al Viro
2026-04-08 23:34   ` Al Viro

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox