From: Yingjie Wang <x-professor@outlook.com>
To: "leitao@debian.org" <leitao@debian.org>
Cc: "a.hindborg@kernel.org" <a.hindborg@kernel.org>,
"viro@zeniv.linux.org.uk" <viro@zeniv.linux.org.uk>,
"linux-fsdevel@vger.kernel.org" <linux-fsdevel@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"stable@vger.kernel.org" <stable@vger.kernel.org>,
Yingjie Wang <1075151112@qq.com>
Subject: [PATCH] configfs: protect symlink source callbacks from removal
Date: Thu, 5 Nov 2026 13:45:17 +0000 [thread overview]
Message-ID: <20261105134514.2890692-1-x-professor@outlook.com> (raw)
From: Yingjie Wang <1075151112@qq.com>
configfs_symlink() drops the source directory inode lock while resolving
its target. A concurrent rmdir() of the source item can take that lock,
detach the item and clear its ci_group in unlink_obj(). configfs_symlink()
then reacquires the lock and calls the source ->allow_link() callback on
the detached item. Its config_item reference keeps the object allocated,
but not attached to the hierarchy.
The UVC gadget format callback reads src->ci_group->cg_subsys to take the
subsystem mutex and faults on the cleared pointer. Racing symlink() into
a UVC format directory with rmdir() of that directory produces:
BUG: KASAN: null-ptr-deref in uvcg_format_allow_link+0x2a/0x140
Read of size 8 at addr 0000000000000060 by task configfs_race/291
Call Trace:
uvcg_format_allow_link+0x2a/0x140
configfs_symlink+0x3f2/0x870
vfs_symlink+0xc0/0x2a0
filename_symlinkat+0x118/0x2b0
__x64_sys_symlink+0x50/0x70
Creating the items needs the privileges required to manage configfs.
Rechecking ci_group after relocking would cover rmdir(), which holds the
source inode lock until unlink_obj() returns. It would not cover
configfs_unregister_group() and configfs_unregister_subsystem(): both
clear ci_group in unlink_group() while the source inode lock is not held,
so the check could become stale before the callback runs.
Take the source fragment read lock after reacquiring the inode lock and
hold it through allow_link(), create_link() and drop_link(). rmdir() and
both unregister paths mark the fragment dead under the write lock before
they unlink the item, so a concurrent removal either waits for the
callback to finish or makes the symlink fail with -ENOENT.
Built from the same configuration, the kernel without this change faulted
within 1,000 iterations, while the kernel with it completed 10,000
iterations with no report. The reproducer covers the rmdir() path; the
unregister paths were checked by code inspection.
Fixes: 351e5d869e5a ("configfs: fix a deadlock in configfs_symlink()")
Cc: stable@vger.kernel.org
Signed-off-by: Yingjie Wang <1075151112@qq.com>
---
The target-side races were fixed by commits a7c1290eef60
("configfs: pin the symlink target's dirent instead of chasing
->ci_dentry") and f06c2d26d199 ("configfs: unhash the dentry before
dropping the item in rmdir"). Those changes protect removal of the link
target. This patch protects the item that receives the link and is
independent.
fs/configfs/symlink.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index 89178f5d371a..a2a69801b14d 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -151,6 +151,7 @@ int configfs_symlink(const struct mnt_idmap *idmap, struct inode *dir,
{
int ret;
struct configfs_dirent *sd;
+ struct configfs_fragment *frag;
struct config_item *parent_item;
struct config_item *target_item = NULL;
struct configfs_dirent *target_sd = NULL;
@@ -207,8 +208,17 @@ int configfs_symlink(const struct mnt_idmap *idmap, struct inode *dir,
else
ret = inode_permission(&nop_mnt_idmap, dir,
MAY_WRITE | MAY_EXEC);
- if (!ret)
- ret = type->ct_item_ops->allow_link(parent_item, target_item);
+ if (ret)
+ goto out_target;
+
+ frag = sd->s_frag;
+ down_read(&frag->frag_sem);
+ if (frag->frag_dead) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+
+ ret = type->ct_item_ops->allow_link(parent_item, target_item);
if (!ret) {
mutex_lock(&configfs_symlink_mutex);
ret = create_link(parent_item, target_item, target_sd, dentry);
@@ -218,6 +228,10 @@ int configfs_symlink(const struct mnt_idmap *idmap, struct inode *dir,
target_item);
}
+out_unlock:
+ up_read(&frag->frag_sem);
+
+out_target:
configfs_put(target_sd);
config_item_put(target_item);
base-commit: 05441d697f7f9a927264f4058cbbc6a03dfcac9d
--
2.43.0
next reply other threads:[~2026-11-05 13:45 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 13:45 Yingjie Wang [this message]
2026-11-06 16:16 ` Breno Leitao
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=20261105134514.2890692-1-x-professor@outlook.com \
--to=x-professor@outlook.com \
--cc=1075151112@qq.com \
--cc=a.hindborg@kernel.org \
--cc=leitao@debian.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=stable@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
all inboxes | Powered by JetHome®