Commit a93fabd3 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix proc_pid_lookup vs exit race

From: Manfred Spraul <manfred@colorfullife.com>

Fixes a race between proc_pid_lookup and sys_exit.

- The inodes and dentries for /proc/<pid>/whatever are cached in the dentry
  cache.  d_revalidate is used to protect against stale data: d_revalidate
  returns invalid if the task exited.

  Additionally, sys_exit flushes the dentries for the task that died -
  otherwise the dentries would stay around until they arrive at the end of
  the LRU, which could take some time.  But there is one race:

  - proc_pid_lookup finds a task and prepares new dentries for it. It must 
    drop all locks for that operation.
  - the process exits, and the /proc/ dentries are flushed. Nothing
    happens, because they are not yet in the hash tables.
  - proc_pid_lookup adds the task to the dentry cache.

  Result: dentry of a dead task in the hash tables.

  The patch fixes that problem by flushing again if proc_pid_lookup notices
  that the thread exited while it created the dentry.  The patch should go
  in, but it's not critical.


- task->proc_dentry must be the dentry of /proc/<pid>.  That way sys_exit
  can flush the whole subtree at exit time.  proc_task_lookup is a direct
  copy of proc_pid_lookup and handles /proc/<>/task/<pid>.  It contains the
  lines that set task->proc_dentry.  This is bogus, and must be removed.

  This hunk is much more critical, because creates a de-facto dentry leak
  (they are recovered after flushing real dentries from the cache).
parent 0f3edb4c
......@@ -1555,6 +1555,7 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
struct inode *inode;
struct proc_inode *ei;
unsigned tgid;
int died;
if (dentry->d_name.len == 4 && !memcmp(dentry->d_name.name,"self",4)) {
inode = new_inode(dir->i_sb);
......@@ -1598,12 +1599,21 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
dentry->d_op = &pid_base_dentry_operations;
died = 0;
d_add(dentry, inode);
spin_lock(&task->proc_lock);
task->proc_dentry = dentry;
d_add(dentry, inode);
if (!pid_alive(task)) {
dentry = proc_pid_unhash(task);
died = 1;
}
spin_unlock(&task->proc_lock);
put_task_struct(task);
if (died) {
proc_pid_flush(dentry);
goto out;
}
return NULL;
out:
return ERR_PTR(-ENOENT);
......@@ -1643,10 +1653,7 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
dentry->d_op = &pid_base_dentry_operations;
spin_lock(&task->proc_lock);
task->proc_dentry = dentry;
d_add(dentry, inode);
spin_unlock(&task->proc_lock);
put_task_struct(task);
return NULL;
......
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