diff options
Diffstat (limited to 'kernel/ptrace.c')
| -rw-r--r-- | kernel/ptrace.c | 163 |
1 files changed, 59 insertions, 104 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index f6d8b8cb5e34..082c320e4dbf 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
| @@ -167,67 +167,82 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
| 167 | int ptrace_attach(struct task_struct *task) | 167 | int ptrace_attach(struct task_struct *task) |
| 168 | { | 168 | { |
| 169 | int retval; | 169 | int retval; |
| 170 | unsigned long flags; | ||
| 171 | 170 | ||
| 172 | audit_ptrace(task); | 171 | audit_ptrace(task); |
| 173 | 172 | ||
| 174 | retval = -EPERM; | 173 | retval = -EPERM; |
| 174 | if (unlikely(task->flags & PF_KTHREAD)) | ||
| 175 | goto out; | ||
| 175 | if (same_thread_group(task, current)) | 176 | if (same_thread_group(task, current)) |
| 176 | goto out; | 177 | goto out; |
| 177 | 178 | ||
| 178 | /* Protect the target's credential calculations against our | 179 | /* |
| 180 | * Protect exec's credential calculations against our interference; | ||
| 179 | * interference; SUID, SGID and LSM creds get determined differently | 181 | * interference; SUID, SGID and LSM creds get determined differently |
| 180 | * under ptrace. | 182 | * under ptrace. |
| 181 | */ | 183 | */ |
| 182 | retval = mutex_lock_interruptible(&task->cred_guard_mutex); | 184 | retval = -ERESTARTNOINTR; |
| 183 | if (retval < 0) | 185 | if (mutex_lock_interruptible(&task->cred_guard_mutex)) |
| 184 | goto out; | 186 | goto out; |
| 185 | 187 | ||
| 186 | retval = -EPERM; | ||
| 187 | repeat: | ||
| 188 | /* | ||
| 189 | * Nasty, nasty. | ||
| 190 | * | ||
| 191 | * We want to hold both the task-lock and the | ||
| 192 | * tasklist_lock for writing at the same time. | ||
| 193 | * But that's against the rules (tasklist_lock | ||
| 194 | * is taken for reading by interrupts on other | ||
| 195 | * cpu's that may have task_lock). | ||
| 196 | */ | ||
| 197 | task_lock(task); | 188 | task_lock(task); |
| 198 | if (!write_trylock_irqsave(&tasklist_lock, flags)) { | ||
| 199 | task_unlock(task); | ||
| 200 | do { | ||
| 201 | cpu_relax(); | ||
| 202 | } while (!write_can_lock(&tasklist_lock)); | ||
| 203 | goto repeat; | ||
| 204 | } | ||
| 205 | |||
| 206 | if (!task->mm) | ||
| 207 | goto bad; | ||
| 208 | /* the same process cannot be attached many times */ | ||
| 209 | if (task->ptrace & PT_PTRACED) | ||
| 210 | goto bad; | ||
| 211 | retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH); | 189 | retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH); |
| 190 | task_unlock(task); | ||
| 212 | if (retval) | 191 | if (retval) |
| 213 | goto bad; | 192 | goto unlock_creds; |
| 214 | 193 | ||
| 215 | /* Go */ | 194 | write_lock_irq(&tasklist_lock); |
| 216 | task->ptrace |= PT_PTRACED; | 195 | retval = -EPERM; |
| 196 | if (unlikely(task->exit_state)) | ||
| 197 | goto unlock_tasklist; | ||
| 198 | if (task->ptrace) | ||
| 199 | goto unlock_tasklist; | ||
| 200 | |||
| 201 | task->ptrace = PT_PTRACED; | ||
| 217 | if (capable(CAP_SYS_PTRACE)) | 202 | if (capable(CAP_SYS_PTRACE)) |
| 218 | task->ptrace |= PT_PTRACE_CAP; | 203 | task->ptrace |= PT_PTRACE_CAP; |
| 219 | 204 | ||
| 220 | __ptrace_link(task, current); | 205 | __ptrace_link(task, current); |
| 221 | |||
| 222 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); | 206 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); |
| 223 | bad: | 207 | |
| 224 | write_unlock_irqrestore(&tasklist_lock, flags); | 208 | retval = 0; |
| 225 | task_unlock(task); | 209 | unlock_tasklist: |
| 210 | write_unlock_irq(&tasklist_lock); | ||
| 211 | unlock_creds: | ||
| 226 | mutex_unlock(&task->cred_guard_mutex); | 212 | mutex_unlock(&task->cred_guard_mutex); |
| 227 | out: | 213 | out: |
| 228 | return retval; | 214 | return retval; |
| 229 | } | 215 | } |
| 230 | 216 | ||
| 217 | /** | ||
| 218 | * ptrace_traceme -- helper for PTRACE_TRACEME | ||
| 219 | * | ||
| 220 | * Performs checks and sets PT_PTRACED. | ||
| 221 | * Should be used by all ptrace implementations for PTRACE_TRACEME. | ||
| 222 | */ | ||
| 223 | int ptrace_traceme(void) | ||
| 224 | { | ||
| 225 | int ret = -EPERM; | ||
| 226 | |||
| 227 | write_lock_irq(&tasklist_lock); | ||
| 228 | /* Are we already being traced? */ | ||
| 229 | if (!current->ptrace) { | ||
| 230 | ret = security_ptrace_traceme(current->parent); | ||
| 231 | /* | ||
| 232 | * Check PF_EXITING to ensure ->real_parent has not passed | ||
| 233 | * exit_ptrace(). Otherwise we don't report the error but | ||
| 234 | * pretend ->real_parent untraces us right after return. | ||
| 235 | */ | ||
| 236 | if (!ret && !(current->real_parent->flags & PF_EXITING)) { | ||
| 237 | current->ptrace = PT_PTRACED; | ||
| 238 | __ptrace_link(current, current->real_parent); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | write_unlock_irq(&tasklist_lock); | ||
| 242 | |||
| 243 | return ret; | ||
| 244 | } | ||
| 245 | |||
| 231 | /* | 246 | /* |
| 232 | * Called with irqs disabled, returns true if childs should reap themselves. | 247 | * Called with irqs disabled, returns true if childs should reap themselves. |
| 233 | */ | 248 | */ |
| @@ -409,37 +424,33 @@ static int ptrace_setoptions(struct task_struct *child, long data) | |||
| 409 | 424 | ||
| 410 | static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info) | 425 | static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info) |
| 411 | { | 426 | { |
| 427 | unsigned long flags; | ||
| 412 | int error = -ESRCH; | 428 | int error = -ESRCH; |
| 413 | 429 | ||
| 414 | read_lock(&tasklist_lock); | 430 | if (lock_task_sighand(child, &flags)) { |
| 415 | if (likely(child->sighand != NULL)) { | ||
| 416 | error = -EINVAL; | 431 | error = -EINVAL; |
| 417 | spin_lock_irq(&child->sighand->siglock); | ||
| 418 | if (likely(child->last_siginfo != NULL)) { | 432 | if (likely(child->last_siginfo != NULL)) { |
| 419 | *info = *child->last_siginfo; | 433 | *info = *child->last_siginfo; |
| 420 | error = 0; | 434 | error = 0; |
| 421 | } | 435 | } |
| 422 | spin_unlock_irq(&child->sighand->siglock); | 436 | unlock_task_sighand(child, &flags); |
| 423 | } | 437 | } |
| 424 | read_unlock(&tasklist_lock); | ||
| 425 | return error; | 438 | return error; |
| 426 | } | 439 | } |
| 427 | 440 | ||
| 428 | static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info) | 441 | static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info) |
| 429 | { | 442 | { |
| 443 | unsigned long flags; | ||
| 430 | int error = -ESRCH; | 444 | int error = -ESRCH; |
| 431 | 445 | ||
| 432 | read_lock(&tasklist_lock); | 446 | if (lock_task_sighand(child, &flags)) { |
| 433 | if (likely(child->sighand != NULL)) { | ||
| 434 | error = -EINVAL; | 447 | error = -EINVAL; |
| 435 | spin_lock_irq(&child->sighand->siglock); | ||
| 436 | if (likely(child->last_siginfo != NULL)) { | 448 | if (likely(child->last_siginfo != NULL)) { |
| 437 | *child->last_siginfo = *info; | 449 | *child->last_siginfo = *info; |
| 438 | error = 0; | 450 | error = 0; |
| 439 | } | 451 | } |
| 440 | spin_unlock_irq(&child->sighand->siglock); | 452 | unlock_task_sighand(child, &flags); |
| 441 | } | 453 | } |
| 442 | read_unlock(&tasklist_lock); | ||
| 443 | return error; | 454 | return error; |
| 444 | } | 455 | } |
| 445 | 456 | ||
| @@ -566,72 +577,16 @@ int ptrace_request(struct task_struct *child, long request, | |||
| 566 | return ret; | 577 | return ret; |
| 567 | } | 578 | } |
| 568 | 579 | ||
| 569 | /** | 580 | static struct task_struct *ptrace_get_task_struct(pid_t pid) |
| 570 | * ptrace_traceme -- helper for PTRACE_TRACEME | ||
| 571 | * | ||
| 572 | * Performs checks and sets PT_PTRACED. | ||
| 573 | * Should be used by all ptrace implementations for PTRACE_TRACEME. | ||
| 574 | */ | ||
| 575 | int ptrace_traceme(void) | ||
| 576 | { | ||
| 577 | int ret = -EPERM; | ||
| 578 | |||
| 579 | /* | ||
| 580 | * Are we already being traced? | ||
| 581 | */ | ||
| 582 | repeat: | ||
| 583 | task_lock(current); | ||
| 584 | if (!(current->ptrace & PT_PTRACED)) { | ||
| 585 | /* | ||
| 586 | * See ptrace_attach() comments about the locking here. | ||
| 587 | */ | ||
| 588 | unsigned long flags; | ||
| 589 | if (!write_trylock_irqsave(&tasklist_lock, flags)) { | ||
| 590 | task_unlock(current); | ||
| 591 | do { | ||
| 592 | cpu_relax(); | ||
| 593 | } while (!write_can_lock(&tasklist_lock)); | ||
| 594 | goto repeat; | ||
| 595 | } | ||
| 596 | |||
| 597 | ret = security_ptrace_traceme(current->parent); | ||
| 598 | |||
| 599 | /* | ||
| 600 | * Check PF_EXITING to ensure ->real_parent has not passed | ||
| 601 | * exit_ptrace(). Otherwise we don't report the error but | ||
| 602 | * pretend ->real_parent untraces us right after return. | ||
| 603 | */ | ||
| 604 | if (!ret && !(current->real_parent->flags & PF_EXITING)) { | ||
| 605 | current->ptrace |= PT_PTRACED; | ||
| 606 | __ptrace_link(current, current->real_parent); | ||
| 607 | } | ||
| 608 | |||
| 609 | write_unlock_irqrestore(&tasklist_lock, flags); | ||
| 610 | } | ||
| 611 | task_unlock(current); | ||
| 612 | return ret; | ||
| 613 | } | ||
| 614 | |||
| 615 | /** | ||
| 616 | * ptrace_get_task_struct -- grab a task struct reference for ptrace | ||
| 617 | * @pid: process id to grab a task_struct reference of | ||
| 618 | * | ||
| 619 | * This function is a helper for ptrace implementations. It checks | ||
| 620 | * permissions and then grabs a task struct for use of the actual | ||
| 621 | * ptrace implementation. | ||
| 622 | * | ||
| 623 | * Returns the task_struct for @pid or an ERR_PTR() on failure. | ||
| 624 | */ | ||
| 625 | struct task_struct *ptrace_get_task_struct(pid_t pid) | ||
| 626 | { | 581 | { |
| 627 | struct task_struct *child; | 582 | struct task_struct *child; |
| 628 | 583 | ||
| 629 | read_lock(&tasklist_lock); | 584 | rcu_read_lock(); |
| 630 | child = find_task_by_vpid(pid); | 585 | child = find_task_by_vpid(pid); |
| 631 | if (child) | 586 | if (child) |
| 632 | get_task_struct(child); | 587 | get_task_struct(child); |
| 588 | rcu_read_unlock(); | ||
| 633 | 589 | ||
| 634 | read_unlock(&tasklist_lock); | ||
| 635 | if (!child) | 590 | if (!child) |
| 636 | return ERR_PTR(-ESRCH); | 591 | return ERR_PTR(-ESRCH); |
| 637 | return child; | 592 | return child; |
