diff options
| author | Andrew Morton <akpm@digeo.com> | 2003-05-25 01:10:37 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-05-25 01:10:37 -0700 |
| commit | 055e188d5ce5d5edd5d916a0cd549ebb195388a3 (patch) | |
| tree | b1f2d4dd72df0d360f528aa8107a62fe47fe5e6d /fs/exec.c | |
| parent | 05cdeac3efd3bbffd5f01a1822c4f941e19ba31d (diff) | |
[PATCH] Fix dcache_lock/tasklist_lock ranking bug
__unhash_process acquires the dcache_lock while holding the
tasklist_lock for writing. This can deadlock. Additionally,
fs/proc/base.c incorrectly assumed that p->pid would be set to 0 during
release_task.
The patch fixes that by adding a new spinlock to the task structure and
fixing all references to (!p->pid).
The alternative to the new spinlock would be to hold dcache_lock around
__unhash_process.
- fs/proc/base.c assumed that p->pid is reset to 0 during exit. This is
not the case anymore. I now look at the count of the pid structure for
PIDTYPE_PID.
- de_thread now tested - as broken as it was before: open handles to
/proc/<pid> are either stale or invalid after an exec of a nptl process,
if the exec was call from a secondary thread.
- a few lock_kernels removed - that part of /proc doesn't need it.
- additional instances of 'if(current->pid)' replaced with pid_alive.
Diffstat (limited to 'fs/exec.c')
| -rw-r--r-- | fs/exec.c | 37 |
1 files changed, 8 insertions, 29 deletions
diff --git a/fs/exec.c b/fs/exec.c index e214e30ca525..9bbd3b4e76dc 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -529,30 +529,6 @@ static int exec_mmap(struct mm_struct *mm) return 0; } -static struct dentry *clean_proc_dentry(struct task_struct *p) -{ - struct dentry *proc_dentry = p->proc_dentry; - - if (proc_dentry) { - spin_lock(&dcache_lock); - if (!d_unhashed(proc_dentry)) { - dget_locked(proc_dentry); - __d_drop(proc_dentry); - } else - proc_dentry = NULL; - spin_unlock(&dcache_lock); - } - return proc_dentry; -} - -static inline void put_proc_dentry(struct dentry *dentry) -{ - if (dentry) { - shrink_dcache_parent(dentry); - dput(dentry); - } -} - /* * This function makes sure the current process has its own signal table, * so that flush_signal_handlers can later reset the handlers without @@ -660,9 +636,11 @@ static inline int de_thread(struct task_struct *tsk) while (leader->state != TASK_ZOMBIE) yield(); + spin_lock(&leader->proc_lock); + spin_lock(¤t->proc_lock); + proc_dentry1 = proc_pid_unhash(current); + proc_dentry2 = proc_pid_unhash(leader); write_lock_irq(&tasklist_lock); - proc_dentry1 = clean_proc_dentry(current); - proc_dentry2 = clean_proc_dentry(leader); if (leader->tgid != current->tgid) BUG(); @@ -702,9 +680,10 @@ static inline int de_thread(struct task_struct *tsk) state = leader->state; write_unlock_irq(&tasklist_lock); - - put_proc_dentry(proc_dentry1); - put_proc_dentry(proc_dentry2); + spin_unlock(&leader->proc_lock); + spin_unlock(¤t->proc_lock); + proc_pid_flush(proc_dentry1); + proc_pid_flush(proc_dentry2); if (state != TASK_ZOMBIE) BUG(); |
