diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 57 |
1 files changed, 39 insertions, 18 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 4e0f0ec003f7..921c22ad16e4 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -148,12 +148,34 @@ 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 | repeat: | ||
159 | /* | ||
160 | * Nasty, nasty. | ||
161 | * | ||
162 | * We want to hold both the task-lock and the | ||
163 | * tasklist_lock for writing at the same time. | ||
164 | * But that's against the rules (tasklist_lock | ||
165 | * is taken for reading by interrupts on other | ||
166 | * cpu's that may have task_lock). | ||
167 | */ | ||
168 | task_lock(task); | ||
169 | local_irq_disable(); | ||
170 | if (!write_trylock(&tasklist_lock)) { | ||
171 | local_irq_enable(); | ||
172 | task_unlock(task); | ||
173 | do { | ||
174 | cpu_relax(); | ||
175 | } while (!write_can_lock(&tasklist_lock)); | ||
176 | goto repeat; | ||
177 | } | ||
178 | |||
157 | /* the same process cannot be attached many times */ | 179 | /* the same process cannot be attached many times */ |
158 | if (task->ptrace & PT_PTRACED) | 180 | if (task->ptrace & PT_PTRACED) |
159 | goto bad; | 181 | goto bad; |
@@ -166,17 +188,15 @@ int ptrace_attach(struct task_struct *task) | |||
166 | ? PT_ATTACHED : 0); | 188 | ? PT_ATTACHED : 0); |
167 | if (capable(CAP_SYS_PTRACE)) | 189 | if (capable(CAP_SYS_PTRACE)) |
168 | task->ptrace |= PT_PTRACE_CAP; | 190 | task->ptrace |= PT_PTRACE_CAP; |
169 | task_unlock(task); | ||
170 | 191 | ||
171 | write_lock_irq(&tasklist_lock); | ||
172 | __ptrace_link(task, current); | 192 | __ptrace_link(task, current); |
173 | write_unlock_irq(&tasklist_lock); | ||
174 | 193 | ||
175 | force_sig_specific(SIGSTOP, task); | 194 | force_sig_specific(SIGSTOP, task); |
176 | return 0; | ||
177 | 195 | ||
178 | bad: | 196 | bad: |
197 | write_unlock_irq(&tasklist_lock); | ||
179 | task_unlock(task); | 198 | task_unlock(task); |
199 | out: | ||
180 | return retval; | 200 | return retval; |
181 | } | 201 | } |
182 | 202 | ||
@@ -417,21 +437,22 @@ int ptrace_request(struct task_struct *child, long request, | |||
417 | */ | 437 | */ |
418 | int ptrace_traceme(void) | 438 | int ptrace_traceme(void) |
419 | { | 439 | { |
420 | int ret; | 440 | int ret = -EPERM; |
421 | 441 | ||
422 | /* | 442 | /* |
423 | * Are we already being traced? | 443 | * Are we already being traced? |
424 | */ | 444 | */ |
425 | if (current->ptrace & PT_PTRACED) | 445 | task_lock(current); |
426 | return -EPERM; | 446 | if (!(current->ptrace & PT_PTRACED)) { |
427 | ret = security_ptrace(current->parent, current); | 447 | ret = security_ptrace(current->parent, current); |
428 | if (ret) | 448 | /* |
429 | return -EPERM; | 449 | * Set the ptrace bit in the process ptrace flags. |
430 | /* | 450 | */ |
431 | * Set the ptrace bit in the process ptrace flags. | 451 | if (!ret) |
432 | */ | 452 | current->ptrace |= PT_PTRACED; |
433 | current->ptrace |= PT_PTRACED; | 453 | } |
434 | return 0; | 454 | task_unlock(current); |
455 | return ret; | ||
435 | } | 456 | } |
436 | 457 | ||
437 | /** | 458 | /** |