| From nobody Mon Sep 17 00:00:00 2001 |
| From: Linus Torvalds <torvalds@g5.osdl.org> |
| Date: Sun May 7 10:49:33 2006 -0700 |
| Subject: Fix ptrace_attach()/ptrace_traceme()/de_thread() race |
| |
| This holds the task lock (and, for ptrace_attach, the tasklist_lock) |
| over the actual attach event, which closes a race between attacking to a |
| thread that is either doing a PTRACE_TRACEME or getting de-threaded. |
| |
| Thanks to Oleg Nesterov for reminding me about this, and Chris Wright |
| for noticing a lost return value in my first version. |
| |
| Signed-off-by: Linus Torvalds <torvalds@osdl.org> |
| Signed-off-by: Chris Wright <chrisw@sous-sol.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| |
| kernel/ptrace.c | 39 +++++++++++++++++++++------------------ |
| 1 file changed, 21 insertions(+), 18 deletions(-) |
| |
| --- linux-2.6.16.16.orig/kernel/ptrace.c |
| +++ linux-2.6.16.16/kernel/ptrace.c |
| @@ -149,12 +149,16 @@ int ptrace_may_attach(struct task_struct |
| int ptrace_attach(struct task_struct *task) |
| { |
| int retval; |
| - task_lock(task); |
| + |
| retval = -EPERM; |
| if (task->pid <= 1) |
| - goto bad; |
| + goto out; |
| if (task->tgid == current->tgid) |
| - goto bad; |
| + goto out; |
| + |
| + write_lock_irq(&tasklist_lock); |
| + task_lock(task); |
| + |
| /* the same process cannot be attached many times */ |
| if (task->ptrace & PT_PTRACED) |
| goto bad; |
| @@ -167,17 +171,15 @@ int ptrace_attach(struct task_struct *ta |
| ? PT_ATTACHED : 0); |
| if (capable(CAP_SYS_PTRACE)) |
| task->ptrace |= PT_PTRACE_CAP; |
| - task_unlock(task); |
| |
| - write_lock_irq(&tasklist_lock); |
| __ptrace_link(task, current); |
| - write_unlock_irq(&tasklist_lock); |
| |
| force_sig_specific(SIGSTOP, task); |
| - return 0; |
| |
| bad: |
| + write_unlock_irq(&tasklist_lock); |
| task_unlock(task); |
| +out: |
| return retval; |
| } |
| |
| @@ -418,21 +420,22 @@ int ptrace_request(struct task_struct *c |
| */ |
| int ptrace_traceme(void) |
| { |
| - int ret; |
| + int ret = -EPERM; |
| |
| /* |
| * Are we already being traced? |
| */ |
| - if (current->ptrace & PT_PTRACED) |
| - return -EPERM; |
| - ret = security_ptrace(current->parent, current); |
| - if (ret) |
| - return -EPERM; |
| - /* |
| - * Set the ptrace bit in the process ptrace flags. |
| - */ |
| - current->ptrace |= PT_PTRACED; |
| - return 0; |
| + task_lock(current); |
| + if (!(current->ptrace & PT_PTRACED)) { |
| + ret = security_ptrace(current->parent, current); |
| + /* |
| + * Set the ptrace bit in the process ptrace flags. |
| + */ |
| + if (!ret) |
| + current->ptrace |= PT_PTRACED; |
| + } |
| + task_unlock(current); |
| + return ret; |
| } |
| |
| /** |