aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/signal.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-03-23 05:37:00 -0400
committerTejun Heo <tj@kernel.org>2011-03-23 05:37:00 -0400
commit5224fa3660ad3881d2f2ad726d22614117963f10 (patch)
tree1e8806eda6058bf019f06c8500ec293a757f9331 /kernel/signal.c
parent0ae8ce1c8c5b9007ce6bfc83ec2aa0dfce5bbed3 (diff)
ptrace: Make do_signal_stop() use ptrace_stop() if the task is being ptraced
A ptraced task would still stop at do_signal_stop() when it's stopping for stop signals and do_signal_stop() behaves the same whether the task is ptraced or not. However, in addition to stopping, ptrace_stop() also does ptrace specific stuff like calling architecture specific callbacks, so this behavior makes the code more fragile and difficult to understand. This patch makes do_signal_stop() test whether the task is ptraced and use ptrace_stop() if so. This renders tracehook_notify_jctl() rather pointless as the ptrace notification is now handled by ptrace_stop() regardless of the return value from the tracehook. It probably is a good idea to update it. This doesn't solve the whole problem as tasks already in stopped state would stay in the regular stop when ptrace attached. That part will be handled by the next patch. Oleg pointed out that this makes a userland-visible change. Before, SIGCONT would be able to wake up a task in group stop even if the task is ptraced if the tracer hasn't issued another ptrace command afterwards (as the next ptrace commands transitions the state into TASK_TRACED which ignores SIGCONT wakeups). With this and the next patch, SIGCONT may race with the transition into TASK_TRACED and is ignored if the tracee already entered TASK_TRACED. Another userland visible change of this and the next patch is that the ptracee's state would now be TASK_TRACED where it used to be TASK_STOPPED, which is visible via fs/proc. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Oleg Nesterov <oleg@redhat.com> Cc: Roland McGrath <roland@redhat.com> Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r--kernel/signal.c43
1 files changed, 25 insertions, 18 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index 9f36dd2e8d5a..418776c41d24 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1783,7 +1783,6 @@ void ptrace_notify(int exit_code)
1783static int do_signal_stop(int signr) 1783static int do_signal_stop(int signr)
1784{ 1784{
1785 struct signal_struct *sig = current->signal; 1785 struct signal_struct *sig = current->signal;
1786 int notify = 0;
1787 1786
1788 if (!(current->group_stop & GROUP_STOP_PENDING)) { 1787 if (!(current->group_stop & GROUP_STOP_PENDING)) {
1789 unsigned int gstop = GROUP_STOP_PENDING | GROUP_STOP_CONSUME; 1788 unsigned int gstop = GROUP_STOP_PENDING | GROUP_STOP_CONSUME;
@@ -1813,29 +1812,37 @@ static int do_signal_stop(int signr)
1813 } else 1812 } else
1814 task_clear_group_stop_pending(t); 1813 task_clear_group_stop_pending(t);
1815 } 1814 }
1816 /*
1817 * If there are no other threads in the group, or if there is
1818 * a group stop in progress and we are the last to stop, report
1819 * to the parent. When ptraced, every thread reports itself.
1820 */
1821 if (task_participate_group_stop(current))
1822 notify = CLD_STOPPED;
1823 if (task_ptrace(current))
1824 notify = CLD_STOPPED;
1825 1815
1826 current->exit_code = sig->group_exit_code; 1816 current->exit_code = sig->group_exit_code;
1827 __set_current_state(TASK_STOPPED); 1817 __set_current_state(TASK_STOPPED);
1828 1818
1829 spin_unlock_irq(&current->sighand->siglock); 1819 if (likely(!task_ptrace(current))) {
1820 int notify = 0;
1830 1821
1831 if (notify) { 1822 /*
1832 read_lock(&tasklist_lock); 1823 * If there are no other threads in the group, or if there
1833 do_notify_parent_cldstop(current, notify); 1824 * is a group stop in progress and we are the last to stop,
1834 read_unlock(&tasklist_lock); 1825 * report to the parent.
1835 } 1826 */
1827 if (task_participate_group_stop(current))
1828 notify = CLD_STOPPED;
1836 1829
1837 /* Now we don't run again until woken by SIGCONT or SIGKILL */ 1830 spin_unlock_irq(&current->sighand->siglock);
1838 schedule(); 1831
1832 if (notify) {
1833 read_lock(&tasklist_lock);
1834 do_notify_parent_cldstop(current, notify);
1835 read_unlock(&tasklist_lock);
1836 }
1837
1838 /* Now we don't run again until woken by SIGCONT or SIGKILL */
1839 schedule();
1840
1841 spin_lock_irq(&current->sighand->siglock);
1842 } else
1843 ptrace_stop(current->exit_code, CLD_STOPPED, 0, NULL);
1844
1845 spin_unlock_irq(&current->sighand->siglock);
1839 1846
1840 tracehook_finish_jctl(); 1847 tracehook_finish_jctl();
1841 current->exit_code = 0; 1848 current->exit_code = 0;