Commit cd6a3ce9 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Linus Torvalds

[PATCH] proc: Close the race of a process dying durning lookup

proc_lookup and task exiting are not synchronized, although some of the
previous code may have suggested that.  Every time before we reuse a dentry
namei.c calls d_op->derevalidate which prevents us from reusing a stale dcache
entry.  Unfortunately it does not prevent us from returning a stale dcache
entry.  This race has been explicitly plugged in proc_pid_lookup but there is
nothing to confine it to just that proc lookup function.

So to prevent the race I call revalidate explictily in all of the proc lookup
functions after I call d_add, and report an error if the revalidate does not
succeed.

Years ago Al Viro did something similar but those changes got lost in the
churn.
Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 48e6484d
...@@ -1402,6 +1402,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, ...@@ -1402,6 +1402,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry,
{ {
struct task_struct *task = proc_task(dir); struct task_struct *task = proc_task(dir);
unsigned fd = name_to_int(dentry); unsigned fd = name_to_int(dentry);
struct dentry *result = ERR_PTR(-ENOENT);
struct file * file; struct file * file;
struct files_struct * files; struct files_struct * files;
struct inode *inode; struct inode *inode;
...@@ -1441,15 +1442,18 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, ...@@ -1441,15 +1442,18 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry,
ei->op.proc_get_link = proc_fd_link; ei->op.proc_get_link = proc_fd_link;
dentry->d_op = &tid_fd_dentry_operations; dentry->d_op = &tid_fd_dentry_operations;
d_add(dentry, inode); d_add(dentry, inode);
return NULL; /* Close the race of the process dying before we return the dentry */
if (tid_fd_revalidate(dentry, NULL))
result = NULL;
out:
return result;
out_unlock2: out_unlock2:
spin_unlock(&files->file_lock); spin_unlock(&files->file_lock);
put_files_struct(files); put_files_struct(files);
out_unlock: out_unlock:
iput(inode); iput(inode);
out: goto out;
return ERR_PTR(-ENOENT);
} }
static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir); static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir);
...@@ -1549,12 +1553,12 @@ static struct dentry *proc_pident_lookup(struct inode *dir, ...@@ -1549,12 +1553,12 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
struct pid_entry *ents) struct pid_entry *ents)
{ {
struct inode *inode; struct inode *inode;
int error; struct dentry *error;
struct task_struct *task = proc_task(dir); struct task_struct *task = proc_task(dir);
struct pid_entry *p; struct pid_entry *p;
struct proc_inode *ei; struct proc_inode *ei;
error = -ENOENT; error = ERR_PTR(-ENOENT);
inode = NULL; inode = NULL;
if (!pid_alive(task)) if (!pid_alive(task))
...@@ -1569,7 +1573,7 @@ static struct dentry *proc_pident_lookup(struct inode *dir, ...@@ -1569,7 +1573,7 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
if (!p->name) if (!p->name)
goto out; goto out;
error = -EINVAL; error = ERR_PTR(-EINVAL);
inode = proc_pid_make_inode(dir->i_sb, task, p->type); inode = proc_pid_make_inode(dir->i_sb, task, p->type);
if (!inode) if (!inode)
goto out; goto out;
...@@ -1736,14 +1740,16 @@ static struct dentry *proc_pident_lookup(struct inode *dir, ...@@ -1736,14 +1740,16 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
default: default:
printk("procfs: impossible type (%d)",p->type); printk("procfs: impossible type (%d)",p->type);
iput(inode); iput(inode);
return ERR_PTR(-EINVAL); error = ERR_PTR(-EINVAL);
goto out;
} }
dentry->d_op = &pid_dentry_operations; dentry->d_op = &pid_dentry_operations;
d_add(dentry, inode); d_add(dentry, inode);
return NULL; /* Close the race of the process dying before we return the dentry */
if (pid_revalidate(dentry, NULL))
error = NULL;
out: out:
return ERR_PTR(error); return error;
} }
static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){
...@@ -1911,6 +1917,7 @@ out: ...@@ -1911,6 +1917,7 @@ out:
/* SMP-safe */ /* SMP-safe */
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{ {
struct dentry *result = ERR_PTR(-ENOENT);
struct task_struct *task; struct task_struct *task;
struct inode *inode; struct inode *inode;
struct proc_inode *ei; struct proc_inode *ei;
...@@ -1944,12 +1951,9 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct ...@@ -1944,12 +1951,9 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
goto out; goto out;
inode = proc_pid_make_inode(dir->i_sb, task, PROC_TGID_INO); inode = proc_pid_make_inode(dir->i_sb, task, PROC_TGID_INO);
if (!inode)
goto out_put_task;
if (!inode) {
put_task_struct(task);
goto out;
}
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO; inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_tgid_base_inode_operations; inode->i_op = &proc_tgid_base_inode_operations;
inode->i_fop = &proc_tgid_base_operations; inode->i_fop = &proc_tgid_base_operations;
...@@ -1963,21 +1967,20 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct ...@@ -1963,21 +1967,20 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
dentry->d_op = &pid_dentry_operations; dentry->d_op = &pid_dentry_operations;
d_add(dentry, inode); d_add(dentry, inode);
if (!pid_alive(task)) { /* Close the race of the process dying before we return the dentry */
d_drop(dentry); if (pid_revalidate(dentry, NULL))
shrink_dcache_parent(dentry); result = NULL;
goto out;
}
out_put_task:
put_task_struct(task); put_task_struct(task);
return NULL;
out: out:
return ERR_PTR(-ENOENT); return result;
} }
/* SMP-safe */ /* SMP-safe */
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{ {
struct dentry *result = ERR_PTR(-ENOENT);
struct task_struct *task; struct task_struct *task;
struct task_struct *leader = proc_task(dir); struct task_struct *leader = proc_task(dir);
struct inode *inode; struct inode *inode;
...@@ -2015,13 +2018,14 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry ...@@ -2015,13 +2018,14 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
dentry->d_op = &pid_dentry_operations; dentry->d_op = &pid_dentry_operations;
d_add(dentry, inode); d_add(dentry, inode);
/* Close the race of the process dying before we return the dentry */
if (pid_revalidate(dentry, NULL))
result = NULL;
put_task_struct(task);
return NULL;
out_drop_task: out_drop_task:
put_task_struct(task); put_task_struct(task);
out: out:
return ERR_PTR(-ENOENT); return result;
} }
#define PROC_NUMBUF 10 #define PROC_NUMBUF 10
......
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