From nobody Thu Apr 9 04:04:23 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.3]) (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 DE6C63B95F0; Wed, 11 Mar 2026 08:02:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.3 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773216185; cv=none; b=ccbzzUZkljWgxbrVo9BxsyGquCxMisj+i/ZkpQ38rduNer/ZG8d0wWEdcmBLy6uqST3HmIrvmR5cu1UukbKhhv67uRv8tR5tdDtOMbxlQBJN8CPRrT5QbS7ukBzjVSXJvJiib4XAV3ZCjQDlFaMiTZbpp2FhnDaGctPSNzCdh/w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773216185; c=relaxed/simple; bh=XsUWoC9EPjD5hwqv+4TZx1PNxeSzImYrqCClJZKxA2k=; h=Date:From:To:Subject:Content-Type:MIME-Version:Message-ID; b=A/po8fqFU6Qor7kit+yw5rlJSDoBZgPSWagXo8VGXdg6HY9B5oANch7hMD3qL5PXgtCsWvu/53wsd9fCn6RLdD7uztBu9tXsCbJvM2HC46Jl7e/n8XEr09wmTrsgZxCYW6ve3OGX7KXMUPs5PSrLyHdvFkmnV0tYYiDuOzt5Qsk= 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=k8ZIm4HQ; arc=none smtp.client-ip=117.135.210.3 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="k8ZIm4HQ" 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=XsUWoC9EPjD5hwqv+4TZx1PNxeSzImYrqCClJZKxA2k=; b=k 8ZIm4HQZ60PIoW6Sv909aZ2cMMKfCqUSRR/RucyDbd2PDHEm+I8M8v8EopKsq+jf lbNyCa6n+LVws2YX4uLS60AuGWMgtAbRTA9H1GelatJa0m5iAr1ytr5DbdiuRcNI 6aMZw5Ln3k9jzsyHK1ek9/jYOf2WpLa6k0+yI2A8QM= Received: from luckd0g$163.com ( [183.205.138.18] ) by ajax-webmail-wmsvr-40-127 (Coremail) ; Wed, 11 Mar 2026 16:02:41 +0800 (CST) Date: Wed, 11 Mar 2026 16:02:41 +0800 (CST) From: "Jianzhou Zhao" To: linux-kernel@vger.kernel.org, viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz, linux-fsdevel@vger.kernel.org Subject: KCSAN: data-race in __d_drop / retain_dentry 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_Qu2cAf6auUkt5yOYYOkfmU4Rhug7UMO3uf8n24JfPJ9wjA/p2yseUUF9NmPf88CwFTuXvxiGfTNO1/ZAU5BifrwxKVLopU+3NWn0KCfijoSlQw== 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: <49420d2.6c84.19cdbeb5e21.Coremail.luckd0g@163.com> X-Coremail-Locale: zh_CN X-CM-TRANSID: fygvCgD3F5ChIbFpPcN2AA--.39264W X-CM-SenderInfo: poxfyvkqj6il2tof0z/xtbC9gE3wWmxIaHyEwAA34 X-Coremail-Antispam: 1U5529EdanIXcx71UUUUU7vcSsGvfC2KfnxnUU== Content-Type: text/plain; charset="utf-8" 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 th= e 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()` whi= ch 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 =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 __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-g2061f= 18ad76e-dirty #50 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 When a dentry receives its final decrement during a path operation (e.g., i= nside `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 =3D 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 u= nreferenced 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.pp= rev` using standard assignments, while `retain_dentry` inspects `pprev` out= side the dentry's lock protection. The Linux kernel explicitly permits opti= mistic RCU verification of `d_unhashed` within `retain_dentry` given that t= ransient inaccuracies gracefully fall through to a strictly-locked verifica= tion slow-path (`locked: if (dentry->d_lockref.count || retain_dentry(dentr= y, 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.=20 Unfortunately, we were unable to generate a reproducer for this bug. Potential Impact This data race technically threatens architectures or compilers prone to lo= ad/store tearing resulting in a garbled non-NULL `pprev` pointer value snap= shot. Nonetheless, functionally, the impact is minuscule: since `retain_den= try` simply falls back to the properly synchronized path if it wrongly pres= umes 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 d= ecoupling 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 =3D 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_b= l_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); } =20 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