Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+29 −25
Original line number Diff line number Diff line
@@ -1402,6 +1402,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry,
{
	struct task_struct *task = proc_task(dir);
	unsigned fd = name_to_int(dentry);
	struct dentry *result = ERR_PTR(-ENOENT);
	struct file * file;
	struct files_struct * files;
	struct inode *inode;
@@ -1441,15 +1442,18 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry,
	ei->op.proc_get_link = proc_fd_link;
	dentry->d_op = &tid_fd_dentry_operations;
	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:
	spin_unlock(&files->file_lock);
	put_files_struct(files);
out_unlock:
	iput(inode);
out:
	return ERR_PTR(-ENOENT);
	goto out;
}

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,
					 struct pid_entry *ents)
{
	struct inode *inode;
	int error;
	struct dentry *error;
	struct task_struct *task = proc_task(dir);
	struct pid_entry *p;
	struct proc_inode *ei;

	error = -ENOENT;
	error = ERR_PTR(-ENOENT);
	inode = NULL;

	if (!pid_alive(task))
@@ -1569,7 +1573,7 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
	if (!p->name)
		goto out;

	error = -EINVAL;
	error = ERR_PTR(-EINVAL);
	inode = proc_pid_make_inode(dir->i_sb, task, p->type);
	if (!inode)
		goto out;
@@ -1736,14 +1740,16 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
		default:
			printk("procfs: impossible type (%d)",p->type);
			iput(inode);
			return ERR_PTR(-EINVAL);
			error = ERR_PTR(-EINVAL);
			goto out;
	}
	dentry->d_op = &pid_dentry_operations;
	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:
	return ERR_PTR(error);
	return error;
}

static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){
@@ -1911,6 +1917,7 @@ out:
/* SMP-safe */
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 inode *inode;
	struct proc_inode *ei;
@@ -1944,12 +1951,9 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
		goto out;

	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_op = &proc_tgid_base_inode_operations;
	inode->i_fop = &proc_tgid_base_operations;
@@ -1963,21 +1967,20 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
	dentry->d_op = &pid_dentry_operations;

	d_add(dentry, inode);
	if (!pid_alive(task)) {
		d_drop(dentry);
		shrink_dcache_parent(dentry);
		goto out;
	}
	/* Close the race of the process dying before we return the dentry */
	if (pid_revalidate(dentry, NULL))
		result = NULL;

out_put_task:
	put_task_struct(task);
	return NULL;
out:
	return ERR_PTR(-ENOENT);
	return result;
}

/* SMP-safe */
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 *leader = proc_task(dir);
	struct inode *inode;
@@ -2015,13 +2018,14 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
	dentry->d_op = &pid_dentry_operations;

	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:
	put_task_struct(task);
out:
	return ERR_PTR(-ENOENT);
	return result;
}

#define PROC_NUMBUF 10