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).
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);
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);
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;