procfs: fix race between symlink removals and traversals

use_pde()/unuse_pde() in ->follow_link()/->put_link() resp.

Cc: stable@vger.kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 7e0e953b 0db59e59

Changed files
+22 -12
fs
-12
fs/proc/generic.c
··· 19 19 #include <linux/mount.h> 20 20 #include <linux/init.h> 21 21 #include <linux/idr.h> 22 - #include <linux/namei.h> 23 22 #include <linux/bitops.h> 24 23 #include <linux/spinlock.h> 25 24 #include <linux/completion.h> ··· 221 222 ida_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST); 222 223 spin_unlock_irqrestore(&proc_inum_lock, flags); 223 224 } 224 - 225 - static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) 226 - { 227 - nd_set_link(nd, __PDE_DATA(dentry->d_inode)); 228 - return NULL; 229 - } 230 - 231 - static const struct inode_operations proc_link_inode_operations = { 232 - .readlink = generic_readlink, 233 - .follow_link = proc_follow_link, 234 - }; 235 225 236 226 /* 237 227 * Don't create negative dentries here, return -ENOENT by hand
+21
fs/proc/inode.c
··· 23 23 #include <linux/slab.h> 24 24 #include <linux/mount.h> 25 25 #include <linux/magic.h> 26 + #include <linux/namei.h> 26 27 27 28 #include <asm/uaccess.h> 28 29 ··· 393 392 .release = proc_reg_release, 394 393 }; 395 394 #endif 395 + 396 + static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) 397 + { 398 + struct proc_dir_entry *pde = PDE(dentry->d_inode); 399 + if (unlikely(!use_pde(pde))) 400 + return ERR_PTR(-EINVAL); 401 + nd_set_link(nd, pde->data); 402 + return pde; 403 + } 404 + 405 + static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p) 406 + { 407 + unuse_pde(p); 408 + } 409 + 410 + const struct inode_operations proc_link_inode_operations = { 411 + .readlink = generic_readlink, 412 + .follow_link = proc_follow_link, 413 + .put_link = proc_put_link, 414 + }; 396 415 397 416 struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) 398 417 {
+1
fs/proc/internal.h
··· 200 200 int closing; 201 201 struct completion *c; 202 202 }; 203 + extern const struct inode_operations proc_link_inode_operations; 203 204 204 205 extern const struct inode_operations proc_pid_link_inode_operations; 205 206