From: Alexey Dobriyan 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: Alexey Dobriyan Cc: "Eric W. Biederman" Cc: Oleg Nesterov Signed-off-by: Andrew Morton --- fs/proc/generic.c | 2 ++ fs/proc/inode.c | 12 ++++-------- include/linux/proc_fs.h | 3 +++ 3 files changed, 9 insertions(+), 8 deletions(-) diff -puN fs/proc/generic.c~fix-race-between-proc_get_inode-and-remove_proc_entry fs/proc/generic.c --- a/fs/proc/generic.c~fix-race-between-proc_get_inode-and-remove_proc_entry +++ a/fs/proc/generic.c @@ -398,6 +398,7 @@ struct dentry *proc_lookup(struct inode 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 d_add(dentry, inode); return NULL; } + de_put(de); return ERR_PTR(error); } diff -puN fs/proc/inode.c~fix-race-between-proc_get_inode-and-remove_proc_entry fs/proc/inode.c --- a/fs/proc/inode.c~fix-race-between-proc_get_inode-and-remove_proc_entry +++ a/fs/proc/inode.c @@ -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 /* * 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 supe { 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->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 * out_no_root: printk("proc_read_super: get root inode failed\n"); iput(root_inode); + de_put(&proc_root); return -ENOMEM; } MODULE_LICENSE("GPL"); diff -puN include/linux/proc_fs.h~fix-race-between-proc_get_inode-and-remove_proc_entry include/linux/proc_fs.h --- a/include/linux/proc_fs.h~fix-race-between-proc_get_inode-and-remove_proc_entry +++ a/include/linux/proc_fs.h @@ -105,6 +105,9 @@ unsigned long task_vsize(struct mm_struc int task_statm(struct mm_struct *, int *, int *, int *, int *); char *task_mem(struct mm_struct *, char *); +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); _