aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2019-05-28 19:46:37 -0400
committerEric W. Biederman <ebiederm@xmission.com>2019-05-30 07:38:29 -0400
commitf6e2aa91a46d2bc79fce9b93a988dbe7655c90c0 (patch)
tree76f9fce8ca98bb6915592fddce1c82db1bfd099e /kernel
parenta188339ca5a396acc588e5851ed7e19f66b0ebd9 (diff)
signal/ptrace: Don't leak unitialized kernel memory with PTRACE_PEEK_SIGINFO
Recently syzbot in conjunction with KMSAN reported that ptrace_peek_siginfo can copy an uninitialized siginfo to userspace. Inspecting ptrace_peek_siginfo confirms this. The problem is that off when initialized from args.off can be initialized to a negaive value. At which point the "if (off >= 0)" test to see if off became negative fails because off started off negative. Prevent the core problem by adding a variable found that is only true if a siginfo is found and copied to a temporary in preparation for being copied to userspace. Prevent args.off from being truncated when being assigned to off by testing that off is <= the maximum possible value of off. Convert off to an unsigned long so that we should not have to truncate args.off, we have well defined overflow behavior so if we add another check we won't risk fighting undefined compiler behavior, and so that we have a type whose maximum value is easy to test for. Cc: Andrei Vagin <avagin@gmail.com> Cc: stable@vger.kernel.org Reported-by: syzbot+0d602a1b0d8c95bdf299@syzkaller.appspotmail.com Fixes: 84c751bd4aeb ("ptrace: add ability to retrieve signals without removing from a queue (v4)") Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/ptrace.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 6f357f4fc859..02c6528ead5c 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -704,6 +704,10 @@ static int ptrace_peek_siginfo(struct task_struct *child,
704 if (arg.nr < 0) 704 if (arg.nr < 0)
705 return -EINVAL; 705 return -EINVAL;
706 706
707 /* Ensure arg.off fits in an unsigned long */
708 if (arg.off > ULONG_MAX)
709 return 0;
710
707 if (arg.flags & PTRACE_PEEKSIGINFO_SHARED) 711 if (arg.flags & PTRACE_PEEKSIGINFO_SHARED)
708 pending = &child->signal->shared_pending; 712 pending = &child->signal->shared_pending;
709 else 713 else
@@ -711,18 +715,20 @@ static int ptrace_peek_siginfo(struct task_struct *child,
711 715
712 for (i = 0; i < arg.nr; ) { 716 for (i = 0; i < arg.nr; ) {
713 kernel_siginfo_t info; 717 kernel_siginfo_t info;
714 s32 off = arg.off + i; 718 unsigned long off = arg.off + i;
719 bool found = false;
715 720
716 spin_lock_irq(&child->sighand->siglock); 721 spin_lock_irq(&child->sighand->siglock);
717 list_for_each_entry(q, &pending->list, list) { 722 list_for_each_entry(q, &pending->list, list) {
718 if (!off--) { 723 if (!off--) {
724 found = true;
719 copy_siginfo(&info, &q->info); 725 copy_siginfo(&info, &q->info);
720 break; 726 break;
721 } 727 }
722 } 728 }
723 spin_unlock_irq(&child->sighand->siglock); 729 spin_unlock_irq(&child->sighand->siglock);
724 730
725 if (off >= 0) /* beyond the end of the list */ 731 if (!found) /* beyond the end of the list */
726 break; 732 break;
727 733
728#ifdef CONFIG_COMPAT 734#ifdef CONFIG_COMPAT