diff options
author | Alexey Dobriyan <adobriyan@openvz.org> | 2007-05-08 03:25:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:01 -0400 |
commit | 7695650a924a6859910c8c19dfa43b4d08224d66 (patch) | |
tree | 5947c3e1b24600b6440468c11b30feeef31eee2c | |
parent | 79c0b2df79eb56fc71e54c75cd7fb3acf84370f9 (diff) |
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: Alexey Dobriyan <adobriyan@openvz.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/proc/generic.c | 2 | ||||
-rw-r--r-- | fs/proc/inode.c | 12 | ||||
-rw-r--r-- | include/linux/proc_fs.h | 3 |
3 files changed, 9 insertions, 8 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 775fb21294d8..22a08ff3475d 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -398,6 +398,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam | |||
398 | if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { | 398 | if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { |
399 | unsigned int ino = de->low_ino; | 399 | unsigned int ino = de->low_ino; |
400 | 400 | ||
401 | de_get(de); | ||
401 | spin_unlock(&proc_subdir_lock); | 402 | spin_unlock(&proc_subdir_lock); |
402 | error = -EINVAL; | 403 | error = -EINVAL; |
403 | inode = proc_get_inode(dir->i_sb, ino, de); | 404 | 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 | d_add(dentry, inode); | 415 | d_add(dentry, inode); |
415 | return NULL; | 416 | return NULL; |
416 | } | 417 | } |
418 | de_put(de); | ||
417 | return ERR_PTR(error); | 419 | return ERR_PTR(error); |
418 | } | 420 | } |
419 | 421 | ||
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 22b1158389ae..d1de6378930c 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -21,7 +21,7 @@ | |||
21 | 21 | ||
22 | #include "internal.h" | 22 | #include "internal.h" |
23 | 23 | ||
24 | static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de) | 24 | struct proc_dir_entry *de_get(struct proc_dir_entry *de) |
25 | { | 25 | { |
26 | if (de) | 26 | if (de) |
27 | atomic_inc(&de->count); | 27 | atomic_inc(&de->count); |
@@ -31,7 +31,7 @@ static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de) | |||
31 | /* | 31 | /* |
32 | * Decrements the use count and checks for deferred deletion. | 32 | * Decrements the use count and checks for deferred deletion. |
33 | */ | 33 | */ |
34 | static void de_put(struct proc_dir_entry *de) | 34 | void de_put(struct proc_dir_entry *de) |
35 | { | 35 | { |
36 | if (de) { | 36 | if (de) { |
37 | lock_kernel(); | 37 | lock_kernel(); |
@@ -146,11 +146,6 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, | |||
146 | { | 146 | { |
147 | struct inode * inode; | 147 | struct inode * inode; |
148 | 148 | ||
149 | /* | ||
150 | * Increment the use count so the dir entry can't disappear. | ||
151 | */ | ||
152 | de_get(de); | ||
153 | |||
154 | WARN_ON(de && de->deleted); | 149 | WARN_ON(de && de->deleted); |
155 | 150 | ||
156 | if (de != NULL && !try_module_get(de->owner)) | 151 | if (de != NULL && !try_module_get(de->owner)) |
@@ -184,7 +179,6 @@ out_ino: | |||
184 | if (de != NULL) | 179 | if (de != NULL) |
185 | module_put(de->owner); | 180 | module_put(de->owner); |
186 | out_mod: | 181 | out_mod: |
187 | de_put(de); | ||
188 | return NULL; | 182 | return NULL; |
189 | } | 183 | } |
190 | 184 | ||
@@ -199,6 +193,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent) | |||
199 | s->s_op = &proc_sops; | 193 | s->s_op = &proc_sops; |
200 | s->s_time_gran = 1; | 194 | s->s_time_gran = 1; |
201 | 195 | ||
196 | de_get(&proc_root); | ||
202 | root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root); | 197 | root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root); |
203 | if (!root_inode) | 198 | if (!root_inode) |
204 | goto out_no_root; | 199 | goto out_no_root; |
@@ -212,6 +207,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent) | |||
212 | out_no_root: | 207 | out_no_root: |
213 | printk("proc_read_super: get root inode failed\n"); | 208 | printk("proc_read_super: get root inode failed\n"); |
214 | iput(root_inode); | 209 | iput(root_inode); |
210 | de_put(&proc_root); | ||
215 | return -ENOMEM; | 211 | return -ENOMEM; |
216 | } | 212 | } |
217 | MODULE_LICENSE("GPL"); | 213 | MODULE_LICENSE("GPL"); |
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index f4f7a63cae1f..3469f96bc8b2 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h | |||
@@ -106,6 +106,9 @@ int task_statm(struct mm_struct *, int *, int *, int *, int *); | |||
106 | char *task_mem(struct mm_struct *, char *); | 106 | char *task_mem(struct mm_struct *, char *); |
107 | void clear_refs_smap(struct mm_struct *mm); | 107 | void clear_refs_smap(struct mm_struct *mm); |
108 | 108 | ||
109 | struct proc_dir_entry *de_get(struct proc_dir_entry *de); | ||
110 | void de_put(struct proc_dir_entry *de); | ||
111 | |||
109 | extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, | 112 | extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, |
110 | struct proc_dir_entry *parent); | 113 | struct proc_dir_entry *parent); |
111 | extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent); | 114 | extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent); |