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

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace

Pull sysctl fix from Eric Biederman:
"A rather embarassing and hard to hit bug was merged into 4.11-rc1.

Andrei Vagin tracked this bug now and after some staring at the code
I came up with a fix"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
proc: Fix proc_sys_prune_dcache to hold a sb reference

+32 -15
+1 -1
fs/proc/internal.h
··· 67 67 struct proc_dir_entry *pde; 68 68 struct ctl_table_header *sysctl; 69 69 struct ctl_table *sysctl_entry; 70 - struct list_head sysctl_inodes; 70 + struct hlist_node sysctl_inodes; 71 71 const struct proc_ns_operations *ns_ops; 72 72 struct inode vfs_inode; 73 73 };
+30 -13
fs/proc/proc_sysctl.c
··· 191 191 head->set = set; 192 192 head->parent = NULL; 193 193 head->node = node; 194 - INIT_LIST_HEAD(&head->inodes); 194 + INIT_HLIST_HEAD(&head->inodes); 195 195 if (node) { 196 196 struct ctl_table *entry; 197 197 for (entry = table; entry->procname; entry++, node++) ··· 261 261 complete(p->unregistering); 262 262 } 263 263 264 - /* called under sysctl_lock */ 265 264 static void proc_sys_prune_dcache(struct ctl_table_header *head) 266 265 { 267 - struct inode *inode, *prev = NULL; 266 + struct inode *inode; 268 267 struct proc_inode *ei; 268 + struct hlist_node *node; 269 + struct super_block *sb; 269 270 270 271 rcu_read_lock(); 271 - list_for_each_entry_rcu(ei, &head->inodes, sysctl_inodes) { 272 - inode = igrab(&ei->vfs_inode); 273 - if (inode) { 274 - rcu_read_unlock(); 275 - iput(prev); 276 - prev = inode; 277 - d_prune_aliases(inode); 272 + for (;;) { 273 + node = hlist_first_rcu(&head->inodes); 274 + if (!node) 275 + break; 276 + ei = hlist_entry(node, struct proc_inode, sysctl_inodes); 277 + spin_lock(&sysctl_lock); 278 + hlist_del_init_rcu(&ei->sysctl_inodes); 279 + spin_unlock(&sysctl_lock); 280 + 281 + inode = &ei->vfs_inode; 282 + sb = inode->i_sb; 283 + if (!atomic_inc_not_zero(&sb->s_active)) 284 + continue; 285 + inode = igrab(inode); 286 + rcu_read_unlock(); 287 + if (unlikely(!inode)) { 288 + deactivate_super(sb); 278 289 rcu_read_lock(); 290 + continue; 279 291 } 292 + 293 + d_prune_aliases(inode); 294 + iput(inode); 295 + deactivate_super(sb); 296 + 297 + rcu_read_lock(); 280 298 } 281 299 rcu_read_unlock(); 282 - iput(prev); 283 300 } 284 301 285 302 /* called under sysctl_lock, will reacquire if has to wait */ ··· 478 461 } 479 462 ei->sysctl = head; 480 463 ei->sysctl_entry = table; 481 - list_add_rcu(&ei->sysctl_inodes, &head->inodes); 464 + hlist_add_head_rcu(&ei->sysctl_inodes, &head->inodes); 482 465 head->count++; 483 466 spin_unlock(&sysctl_lock); 484 467 ··· 506 489 void proc_sys_evict_inode(struct inode *inode, struct ctl_table_header *head) 507 490 { 508 491 spin_lock(&sysctl_lock); 509 - list_del_rcu(&PROC_I(inode)->sysctl_inodes); 492 + hlist_del_init_rcu(&PROC_I(inode)->sysctl_inodes); 510 493 if (!--head->count) 511 494 kfree_rcu(head, rcu); 512 495 spin_unlock(&sysctl_lock);
+1 -1
include/linux/sysctl.h
··· 143 143 struct ctl_table_set *set; 144 144 struct ctl_dir *parent; 145 145 struct ctl_node *node; 146 - struct list_head inodes; /* head for proc_inode->sysctl_inodes */ 146 + struct hlist_head inodes; /* head for proc_inode->sysctl_inodes */ 147 147 }; 148 148 149 149 struct ctl_dir {