aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/ptrace.c20
1 files changed, 20 insertions, 0 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 227fec36b12a..9a34bd80a745 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -697,6 +697,8 @@ static int ptrace_peek_siginfo(struct task_struct *child,
697static int ptrace_resume(struct task_struct *child, long request, 697static int ptrace_resume(struct task_struct *child, long request,
698 unsigned long data) 698 unsigned long data)
699{ 699{
700 bool need_siglock;
701
700 if (!valid_signal(data)) 702 if (!valid_signal(data))
701 return -EIO; 703 return -EIO;
702 704
@@ -724,8 +726,26 @@ static int ptrace_resume(struct task_struct *child, long request,
724 user_disable_single_step(child); 726 user_disable_single_step(child);
725 } 727 }
726 728
729 /*
730 * Change ->exit_code and ->state under siglock to avoid the race
731 * with wait_task_stopped() in between; a non-zero ->exit_code will
732 * wrongly look like another report from tracee.
733 *
734 * Note that we need siglock even if ->exit_code == data and/or this
735 * status was not reported yet, the new status must not be cleared by
736 * wait_task_stopped() after resume.
737 *
738 * If data == 0 we do not care if wait_task_stopped() reports the old
739 * status and clears the code too; this can't race with the tracee, it
740 * takes siglock after resume.
741 */
742 need_siglock = data && !thread_group_empty(current);
743 if (need_siglock)
744 spin_lock_irq(&child->sighand->siglock);
727 child->exit_code = data; 745 child->exit_code = data;
728 wake_up_state(child, __TASK_TRACED); 746 wake_up_state(child, __TASK_TRACED);
747 if (need_siglock)
748 spin_unlock_irq(&child->sighand->siglock);
729 749
730 return 0; 750 return 0;
731} 751}