diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-05-07 13:49:33 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-05-07 13:49:33 -0400 |
commit | f5b40e363ad6041a96e3da32281d8faa191597b9 (patch) | |
tree | 5f35453cff7acd5df5622a108381bbd011a383c5 /kernel/ptrace.c | |
parent | 5528e568a760442e0ec8fd2dea1f0791875a066b (diff) |
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>
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 39 |
1 files changed, 21 insertions, 18 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 4e0f0ec003f7..b0f8da80d7d4 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -148,12 +148,16 @@ int ptrace_may_attach(struct task_struct *task) | |||
148 | int ptrace_attach(struct task_struct *task) | 148 | int ptrace_attach(struct task_struct *task) |
149 | { | 149 | { |
150 | int retval; | 150 | int retval; |
151 | task_lock(task); | 151 | |
152 | retval = -EPERM; | 152 | retval = -EPERM; |
153 | if (task->pid <= 1) | 153 | if (task->pid <= 1) |
154 | goto bad; | 154 | goto out; |
155 | if (task->tgid == current->tgid) | 155 | if (task->tgid == current->tgid) |
156 | goto bad; | 156 | goto out; |
157 | |||
158 | write_lock_irq(&tasklist_lock); | ||
159 | task_lock(task); | ||
160 | |||
157 | /* the same process cannot be attached many times */ | 161 | /* the same process cannot be attached many times */ |
158 | if (task->ptrace & PT_PTRACED) | 162 | if (task->ptrace & PT_PTRACED) |
159 | goto bad; | 163 | goto bad; |
@@ -166,17 +170,15 @@ int ptrace_attach(struct task_struct *task) | |||
166 | ? PT_ATTACHED : 0); | 170 | ? PT_ATTACHED : 0); |
167 | if (capable(CAP_SYS_PTRACE)) | 171 | if (capable(CAP_SYS_PTRACE)) |
168 | task->ptrace |= PT_PTRACE_CAP; | 172 | task->ptrace |= PT_PTRACE_CAP; |
169 | task_unlock(task); | ||
170 | 173 | ||
171 | write_lock_irq(&tasklist_lock); | ||
172 | __ptrace_link(task, current); | 174 | __ptrace_link(task, current); |
173 | write_unlock_irq(&tasklist_lock); | ||
174 | 175 | ||
175 | force_sig_specific(SIGSTOP, task); | 176 | force_sig_specific(SIGSTOP, task); |
176 | return 0; | ||
177 | 177 | ||
178 | bad: | 178 | bad: |
179 | write_unlock_irq(&tasklist_lock); | ||
179 | task_unlock(task); | 180 | task_unlock(task); |
181 | out: | ||
180 | return retval; | 182 | return retval; |
181 | } | 183 | } |
182 | 184 | ||
@@ -417,21 +419,22 @@ int ptrace_request(struct task_struct *child, long request, | |||
417 | */ | 419 | */ |
418 | int ptrace_traceme(void) | 420 | int ptrace_traceme(void) |
419 | { | 421 | { |
420 | int ret; | 422 | int ret = -EPERM; |
421 | 423 | ||
422 | /* | 424 | /* |
423 | * Are we already being traced? | 425 | * Are we already being traced? |
424 | */ | 426 | */ |
425 | if (current->ptrace & PT_PTRACED) | 427 | task_lock(current); |
426 | return -EPERM; | 428 | if (!(current->ptrace & PT_PTRACED)) { |
427 | ret = security_ptrace(current->parent, current); | 429 | ret = security_ptrace(current->parent, current); |
428 | if (ret) | 430 | /* |
429 | return -EPERM; | 431 | * Set the ptrace bit in the process ptrace flags. |
430 | /* | 432 | */ |
431 | * Set the ptrace bit in the process ptrace flags. | 433 | if (!ret) |
432 | */ | 434 | current->ptrace |= PT_PTRACED; |
433 | current->ptrace |= PT_PTRACED; | 435 | } |
434 | return 0; | 436 | task_unlock(current); |
437 | return ret; | ||
435 | } | 438 | } |
436 | 439 | ||
437 | /** | 440 | /** |