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

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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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)) {
				unsigned int ino = de->low_ino;

				de_get(de);
				spin_unlock(&proc_subdir_lock);
				error = -EINVAL;
				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
		d_add(dentry, inode);
		return NULL;
	}
	de_put(de);
	return ERR_PTR(error);
}

+4 −8
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@

#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)
		atomic_inc(&de->count);
@@ -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.
 */
static void de_put(struct proc_dir_entry *de)
void de_put(struct proc_dir_entry *de)
{
	if (de) {	
		lock_kernel();		
@@ -146,11 +146,6 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
{
	struct inode * inode;

	/*
	 * Increment the use count so the dir entry can't disappear.
	 */
	de_get(de);

	WARN_ON(de && de->deleted);

	if (de != NULL && !try_module_get(de->owner))
@@ -184,7 +179,6 @@ out_ino:
	if (de != NULL)
		module_put(de->owner);
out_mod:
	de_put(de);
	return NULL;
}			

@@ -199,6 +193,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent)
	s->s_op = &proc_sops;
	s->s_time_gran = 1;
	
	de_get(&proc_root);
	root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
	if (!root_inode)
		goto out_no_root;
@@ -212,6 +207,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent)
out_no_root:
	printk("proc_read_super: get root inode failed\n");
	iput(root_inode);
	de_put(&proc_root);
	return -ENOMEM;
}
MODULE_LICENSE("GPL");
+3 −0
Original line number Diff line number Diff line
@@ -106,6 +106,9 @@ int task_statm(struct mm_struct *, int *, int *, int *, int *);
char *task_mem(struct mm_struct *, char *);
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,
						struct proc_dir_entry *parent);
extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);