diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 101 |
1 files changed, 51 insertions, 50 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 9439bd3331a6..12e21a949db1 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -177,12 +177,13 @@ int ptrace_attach(struct task_struct *task) | |||
177 | if (same_thread_group(task, current)) | 177 | if (same_thread_group(task, current)) |
178 | goto out; | 178 | goto out; |
179 | 179 | ||
180 | /* Protect the target's credential calculations against our | 180 | /* |
181 | * Protect exec's credential calculations against our interference; | ||
181 | * interference; SUID, SGID and LSM creds get determined differently | 182 | * interference; SUID, SGID and LSM creds get determined differently |
182 | * under ptrace. | 183 | * under ptrace. |
183 | */ | 184 | */ |
184 | retval = mutex_lock_interruptible(&task->cred_guard_mutex); | 185 | retval = mutex_lock_interruptible(&task->cred_guard_mutex); |
185 | if (retval < 0) | 186 | if (retval < 0) |
186 | goto out; | 187 | goto out; |
187 | repeat: | 188 | repeat: |
188 | /* | 189 | /* |
@@ -210,10 +211,10 @@ repeat: | |||
210 | retval = -EPERM; | 211 | retval = -EPERM; |
211 | if (unlikely(task->exit_state)) | 212 | if (unlikely(task->exit_state)) |
212 | goto bad; | 213 | goto bad; |
213 | if (task->ptrace & PT_PTRACED) | 214 | if (task->ptrace) |
214 | goto bad; | 215 | goto bad; |
215 | 216 | ||
216 | task->ptrace |= PT_PTRACED; | 217 | task->ptrace = PT_PTRACED; |
217 | if (capable(CAP_SYS_PTRACE)) | 218 | if (capable(CAP_SYS_PTRACE)) |
218 | task->ptrace |= PT_PTRACE_CAP; | 219 | task->ptrace |= PT_PTRACE_CAP; |
219 | 220 | ||
@@ -229,6 +230,52 @@ out: | |||
229 | return retval; | 230 | return retval; |
230 | } | 231 | } |
231 | 232 | ||
233 | /** | ||
234 | * ptrace_traceme -- helper for PTRACE_TRACEME | ||
235 | * | ||
236 | * Performs checks and sets PT_PTRACED. | ||
237 | * Should be used by all ptrace implementations for PTRACE_TRACEME. | ||
238 | */ | ||
239 | int ptrace_traceme(void) | ||
240 | { | ||
241 | int ret = -EPERM; | ||
242 | |||
243 | /* | ||
244 | * Are we already being traced? | ||
245 | */ | ||
246 | repeat: | ||
247 | task_lock(current); | ||
248 | if (!current->ptrace) { | ||
249 | /* | ||
250 | * See ptrace_attach() comments about the locking here. | ||
251 | */ | ||
252 | unsigned long flags; | ||
253 | if (!write_trylock_irqsave(&tasklist_lock, flags)) { | ||
254 | task_unlock(current); | ||
255 | do { | ||
256 | cpu_relax(); | ||
257 | } while (!write_can_lock(&tasklist_lock)); | ||
258 | goto repeat; | ||
259 | } | ||
260 | |||
261 | ret = security_ptrace_traceme(current->parent); | ||
262 | |||
263 | /* | ||
264 | * Check PF_EXITING to ensure ->real_parent has not passed | ||
265 | * exit_ptrace(). Otherwise we don't report the error but | ||
266 | * pretend ->real_parent untraces us right after return. | ||
267 | */ | ||
268 | if (!ret && !(current->real_parent->flags & PF_EXITING)) { | ||
269 | current->ptrace = PT_PTRACED; | ||
270 | __ptrace_link(current, current->real_parent); | ||
271 | } | ||
272 | |||
273 | write_unlock_irqrestore(&tasklist_lock, flags); | ||
274 | } | ||
275 | task_unlock(current); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
232 | /* | 279 | /* |
233 | * Called with irqs disabled, returns true if childs should reap themselves. | 280 | * Called with irqs disabled, returns true if childs should reap themselves. |
234 | */ | 281 | */ |
@@ -568,52 +615,6 @@ int ptrace_request(struct task_struct *child, long request, | |||
568 | } | 615 | } |
569 | 616 | ||
570 | /** | 617 | /** |
571 | * ptrace_traceme -- helper for PTRACE_TRACEME | ||
572 | * | ||
573 | * Performs checks and sets PT_PTRACED. | ||
574 | * Should be used by all ptrace implementations for PTRACE_TRACEME. | ||
575 | */ | ||
576 | int ptrace_traceme(void) | ||
577 | { | ||
578 | int ret = -EPERM; | ||
579 | |||
580 | /* | ||
581 | * Are we already being traced? | ||
582 | */ | ||
583 | repeat: | ||
584 | task_lock(current); | ||
585 | if (!(current->ptrace & PT_PTRACED)) { | ||
586 | /* | ||
587 | * See ptrace_attach() comments about the locking here. | ||
588 | */ | ||
589 | unsigned long flags; | ||
590 | if (!write_trylock_irqsave(&tasklist_lock, flags)) { | ||
591 | task_unlock(current); | ||
592 | do { | ||
593 | cpu_relax(); | ||
594 | } while (!write_can_lock(&tasklist_lock)); | ||
595 | goto repeat; | ||
596 | } | ||
597 | |||
598 | ret = security_ptrace_traceme(current->parent); | ||
599 | |||
600 | /* | ||
601 | * Check PF_EXITING to ensure ->real_parent has not passed | ||
602 | * exit_ptrace(). Otherwise we don't report the error but | ||
603 | * pretend ->real_parent untraces us right after return. | ||
604 | */ | ||
605 | if (!ret && !(current->real_parent->flags & PF_EXITING)) { | ||
606 | current->ptrace |= PT_PTRACED; | ||
607 | __ptrace_link(current, current->real_parent); | ||
608 | } | ||
609 | |||
610 | write_unlock_irqrestore(&tasklist_lock, flags); | ||
611 | } | ||
612 | task_unlock(current); | ||
613 | return ret; | ||
614 | } | ||
615 | |||
616 | /** | ||
617 | * ptrace_get_task_struct -- grab a task struct reference for ptrace | 618 | * ptrace_get_task_struct -- grab a task struct reference for ptrace |
618 | * @pid: process id to grab a task_struct reference of | 619 | * @pid: process id to grab a task_struct reference of |
619 | * | 620 | * |