diff options
| -rw-r--r-- | kernel/cred.c | 9 | ||||
| -rw-r--r-- | kernel/ptrace.c | 20 |
2 files changed, 27 insertions, 2 deletions
diff --git a/kernel/cred.c b/kernel/cred.c index e74ffdc98a92..c73a87a4df13 100644 --- a/kernel/cred.c +++ b/kernel/cred.c | |||
| @@ -446,6 +446,15 @@ int commit_creds(struct cred *new) | |||
| 446 | if (task->mm) | 446 | if (task->mm) |
| 447 | set_dumpable(task->mm, suid_dumpable); | 447 | set_dumpable(task->mm, suid_dumpable); |
| 448 | task->pdeath_signal = 0; | 448 | task->pdeath_signal = 0; |
| 449 | /* | ||
| 450 | * If a task drops privileges and becomes nondumpable, | ||
| 451 | * the dumpability change must become visible before | ||
| 452 | * the credential change; otherwise, a __ptrace_may_access() | ||
| 453 | * racing with this change may be able to attach to a task it | ||
| 454 | * shouldn't be able to attach to (as if the task had dropped | ||
| 455 | * privileges without becoming nondumpable). | ||
| 456 | * Pairs with a read barrier in __ptrace_may_access(). | ||
| 457 | */ | ||
| 449 | smp_wmb(); | 458 | smp_wmb(); |
| 450 | } | 459 | } |
| 451 | 460 | ||
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 5710d07e67cf..8456b6e2205f 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
| @@ -324,6 +324,16 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
| 324 | return -EPERM; | 324 | return -EPERM; |
| 325 | ok: | 325 | ok: |
| 326 | rcu_read_unlock(); | 326 | rcu_read_unlock(); |
| 327 | /* | ||
| 328 | * If a task drops privileges and becomes nondumpable (through a syscall | ||
| 329 | * like setresuid()) while we are trying to access it, we must ensure | ||
| 330 | * that the dumpability is read after the credentials; otherwise, | ||
| 331 | * we may be able to attach to a task that we shouldn't be able to | ||
| 332 | * attach to (as if the task had dropped privileges without becoming | ||
| 333 | * nondumpable). | ||
| 334 | * Pairs with a write barrier in commit_creds(). | ||
| 335 | */ | ||
| 336 | smp_rmb(); | ||
| 327 | mm = task->mm; | 337 | mm = task->mm; |
| 328 | if (mm && | 338 | if (mm && |
| 329 | ((get_dumpable(mm) != SUID_DUMP_USER) && | 339 | ((get_dumpable(mm) != SUID_DUMP_USER) && |
| @@ -705,6 +715,10 @@ static int ptrace_peek_siginfo(struct task_struct *child, | |||
| 705 | if (arg.nr < 0) | 715 | if (arg.nr < 0) |
| 706 | return -EINVAL; | 716 | return -EINVAL; |
| 707 | 717 | ||
| 718 | /* Ensure arg.off fits in an unsigned long */ | ||
| 719 | if (arg.off > ULONG_MAX) | ||
| 720 | return 0; | ||
| 721 | |||
| 708 | if (arg.flags & PTRACE_PEEKSIGINFO_SHARED) | 722 | if (arg.flags & PTRACE_PEEKSIGINFO_SHARED) |
| 709 | pending = &child->signal->shared_pending; | 723 | pending = &child->signal->shared_pending; |
| 710 | else | 724 | else |
| @@ -712,18 +726,20 @@ static int ptrace_peek_siginfo(struct task_struct *child, | |||
| 712 | 726 | ||
| 713 | for (i = 0; i < arg.nr; ) { | 727 | for (i = 0; i < arg.nr; ) { |
| 714 | kernel_siginfo_t info; | 728 | kernel_siginfo_t info; |
| 715 | s32 off = arg.off + i; | 729 | unsigned long off = arg.off + i; |
| 730 | bool found = false; | ||
| 716 | 731 | ||
| 717 | spin_lock_irq(&child->sighand->siglock); | 732 | spin_lock_irq(&child->sighand->siglock); |
| 718 | list_for_each_entry(q, &pending->list, list) { | 733 | list_for_each_entry(q, &pending->list, list) { |
| 719 | if (!off--) { | 734 | if (!off--) { |
| 735 | found = true; | ||
| 720 | copy_siginfo(&info, &q->info); | 736 | copy_siginfo(&info, &q->info); |
| 721 | break; | 737 | break; |
| 722 | } | 738 | } |
| 723 | } | 739 | } |
| 724 | spin_unlock_irq(&child->sighand->siglock); | 740 | spin_unlock_irq(&child->sighand->siglock); |
| 725 | 741 | ||
| 726 | if (off >= 0) /* beyond the end of the list */ | 742 | if (!found) /* beyond the end of the list */ |
| 727 | break; | 743 | break; |
| 728 | 744 | ||
| 729 | #ifdef CONFIG_COMPAT | 745 | #ifdef CONFIG_COMPAT |
