Skip to content

Commit 9a46fe0

Browse files
ebiedermbwhacks
authored andcommitted
signal/ptrace: Don't leak unitialized kernel memory with PTRACE_PEEK_SIGINFO
commit f6e2aa9 upstream. 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 <[email protected]> Reported-by: [email protected] Fixes: 84c751b ("ptrace: add ability to retrieve signals without removing from a queue (v4)") Signed-off-by: "Eric W. Biederman" <[email protected]> [bwh: Backported to 3.16: adjust context] Signed-off-by: Ben Hutchings <[email protected]>
1 parent 306c114 commit 9a46fe0

File tree

1 file changed

+8
-2
lines changed

1 file changed

+8
-2
lines changed

kernel/ptrace.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,25 +711,31 @@ static int ptrace_peek_siginfo(struct task_struct *child,
711711
if (arg.nr < 0)
712712
return -EINVAL;
713713

714+
/* Ensure arg.off fits in an unsigned long */
715+
if (arg.off > ULONG_MAX)
716+
return 0;
717+
714718
if (arg.flags & PTRACE_PEEKSIGINFO_SHARED)
715719
pending = &child->signal->shared_pending;
716720
else
717721
pending = &child->pending;
718722

719723
for (i = 0; i < arg.nr; ) {
720724
siginfo_t info;
721-
s32 off = arg.off + i;
725+
unsigned long off = arg.off + i;
726+
bool found = false;
722727

723728
spin_lock_irq(&child->sighand->siglock);
724729
list_for_each_entry(q, &pending->list, list) {
725730
if (!off--) {
731+
found = true;
726732
copy_siginfo(&info, &q->info);
727733
break;
728734
}
729735
}
730736
spin_unlock_irq(&child->sighand->siglock);
731737

732-
if (off >= 0) /* beyond the end of the list */
738+
if (!found) /* beyond the end of the list */
733739
break;
734740

735741
#ifdef CONFIG_COMPAT

0 commit comments

Comments
 (0)