Commit 7695650a authored by Alexey Dobriyan's avatar Alexey Dobriyan Committed by Linus Torvalds

Fix race between proc_get_inode() and remove_proc_entry()

proc_lookup				remove_proc_entry
===========				=================

lock_kernel();
spin_lock(&proc_subdir_lock);
[find PDE with refcount 0]
spin_unlock(&proc_subdir_lock);
					spin_lock(&proc_subdir_lock);
					[find PDE with refcount 0]
					[check refcount and free PDE]
					spin_unlock(&proc_subdir_lock);
proc_get_inode:
	de_get(de); /* boom */
Signed-off-by: default avatarAlexey Dobriyan <adobriyan@openvz.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 79c0b2df
...@@ -398,6 +398,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam ...@@ -398,6 +398,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
unsigned int ino = de->low_ino; unsigned int ino = de->low_ino;
de_get(de);
spin_unlock(&proc_subdir_lock); spin_unlock(&proc_subdir_lock);
error = -EINVAL; error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de); inode = proc_get_inode(dir->i_sb, ino, de);
...@@ -414,6 +415,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam ...@@ -414,6 +415,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam
d_add(dentry, inode); d_add(dentry, inode);
return NULL; return NULL;
} }
de_put(de);
return ERR_PTR(error); return ERR_PTR(error);
} }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#include "internal.h" #include "internal.h"
static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de) struct proc_dir_entry *de_get(struct proc_dir_entry *de)
{ {
if (de) if (de)
atomic_inc(&de->count); atomic_inc(&de->count);
...@@ -31,7 +31,7 @@ static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de) ...@@ -31,7 +31,7 @@ static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de)
/* /*
* Decrements the use count and checks for deferred deletion. * Decrements the use count and checks for deferred deletion.
*/ */
static void de_put(struct proc_dir_entry *de) void de_put(struct proc_dir_entry *de)
{ {
if (de) { if (de) {
lock_kernel(); lock_kernel();
...@@ -146,11 +146,6 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, ...@@ -146,11 +146,6 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
{ {
struct inode * inode; struct inode * inode;
/*
* Increment the use count so the dir entry can't disappear.
*/
de_get(de);
WARN_ON(de && de->deleted); WARN_ON(de && de->deleted);
if (de != NULL && !try_module_get(de->owner)) if (de != NULL && !try_module_get(de->owner))
...@@ -184,7 +179,6 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, ...@@ -184,7 +179,6 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
if (de != NULL) if (de != NULL)
module_put(de->owner); module_put(de->owner);
out_mod: out_mod:
de_put(de);
return NULL; return NULL;
} }
...@@ -199,6 +193,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent) ...@@ -199,6 +193,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent)
s->s_op = &proc_sops; s->s_op = &proc_sops;
s->s_time_gran = 1; s->s_time_gran = 1;
de_get(&proc_root);
root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root); root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
if (!root_inode) if (!root_inode)
goto out_no_root; goto out_no_root;
...@@ -212,6 +207,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent) ...@@ -212,6 +207,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent)
out_no_root: out_no_root:
printk("proc_read_super: get root inode failed\n"); printk("proc_read_super: get root inode failed\n");
iput(root_inode); iput(root_inode);
de_put(&proc_root);
return -ENOMEM; return -ENOMEM;
} }
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -106,6 +106,9 @@ int task_statm(struct mm_struct *, int *, int *, int *, int *); ...@@ -106,6 +106,9 @@ int task_statm(struct mm_struct *, int *, int *, int *, int *);
char *task_mem(struct mm_struct *, char *); char *task_mem(struct mm_struct *, char *);
void clear_refs_smap(struct mm_struct *mm); void clear_refs_smap(struct mm_struct *mm);
struct proc_dir_entry *de_get(struct proc_dir_entry *de);
void de_put(struct proc_dir_entry *de);
extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent); struct proc_dir_entry *parent);
extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent); extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment