Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kernfs: Drop kernfs_rwsem while invoking lookup_positive_unlocked().

syzbot reported two warnings:
- kernfs_node::name was accessed outside of a RCU section so it created
warning. The kernfs_rwsem was held so it was okay but it wasn't seen.

- While kernfs_rwsem was held invoked lookup_positive_unlocked()->
kernfs_dop_revalidate() which acquired kernfs_rwsem.

kernfs_rwsem was both acquired as a read lock so it can be acquired
twice. However if a writer acquires the lock after the first reader then
neither the writer nor the second reader can obtain the lock so it
deadlocks.

The reason for the lock is to ensure that kernfs_node::name remain
stable during lookup_positive_unlocked()'s invocation. The function can
not be invoked within a RCU section because it may sleep.

Make a temporary copy of the kernfs_node::name under the lock so
GFP_KERNEL can be used and use this instead.

Reported-by: syzbot+ecccecbc636b455f9084@syzkaller.appspotmail.com
Fixes: 5b2fabf7fe8f ("kernfs: Acquire kernfs_rwsem in kernfs_node_dentry().")
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Acked-by: Tejun Heo <tj@kernel.org>
Link: https://lore.kernel.org/r/20250218163938.xmvjlJ0K@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Sebastian Andrzej Siewior and committed by
Greg Kroah-Hartman
6ef5b6fa 2ce177e9

+25 -10
+25 -10
fs/kernfs/mount.c
··· 220 220 return dentry; 221 221 222 222 root = kernfs_root(kn); 223 - guard(rwsem_read)(&root->kernfs_rwsem); 224 - 225 - knparent = find_next_ancestor(kn, NULL); 226 - if (WARN_ON(!knparent)) { 227 - dput(dentry); 223 + /* 224 + * As long as kn is valid, its parent can not vanish. This is cgroup's 225 + * kn so it not have its parent replaced. Therefore it is safe to use 226 + * the ancestor node outside of the RCU or locked section. 227 + */ 228 + if (WARN_ON_ONCE(!(root->flags & KERNFS_ROOT_INVARIANT_PARENT))) 228 229 return ERR_PTR(-EINVAL); 230 + scoped_guard(rcu) { 231 + knparent = find_next_ancestor(kn, NULL); 232 + if (WARN_ON(!knparent)) { 233 + dput(dentry); 234 + return ERR_PTR(-EINVAL); 235 + } 229 236 } 230 237 231 238 do { ··· 242 235 243 236 if (kn == knparent) 244 237 return dentry; 245 - kntmp = find_next_ancestor(kn, knparent); 246 - if (WARN_ON(!kntmp)) { 247 - dput(dentry); 248 - return ERR_PTR(-EINVAL); 238 + 239 + scoped_guard(rwsem_read, &root->kernfs_rwsem) { 240 + kntmp = find_next_ancestor(kn, knparent); 241 + if (WARN_ON(!kntmp)) { 242 + dput(dentry); 243 + return ERR_PTR(-EINVAL); 244 + } 245 + name = kstrdup(kernfs_rcu_name(kntmp), GFP_KERNEL); 249 246 } 250 - name = rcu_dereference(kntmp->name); 247 + if (!name) { 248 + dput(dentry); 249 + return ERR_PTR(-ENOMEM); 250 + } 251 251 dtmp = lookup_positive_unlocked(name, dentry, strlen(name)); 252 252 dput(dentry); 253 + kfree(name); 253 254 if (IS_ERR(dtmp)) 254 255 return dtmp; 255 256 knparent = kntmp;