aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/signal.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-03-23 05:37:01 -0400
committerTejun Heo <tj@kernel.org>2011-03-23 05:37:01 -0400
commitceb6bd67f9b9db765e1c29405f26e8460391badd (patch)
treed4db6cfe58bf2d84bf6af8f1079619050f6c8adc /kernel/signal.c
parent62bcf9d992ecc19ea4f37ff57ee0b3333e3e843e (diff)
job control: Notify the real parent of job control events regardless of ptrace
With recent changes, job control and ptrace stopped states are properly separated and accessible to the real parent and the ptracer respectively; however, notifications of job control stopped/continued events to the real parent while ptraced are still missing. A ptracee participates in group stop in ptrace_stop() but the completion isn't notified. If participation results in completion of group stop, notify the real parent of the event. The ptrace and group stops are separate and can be handled as such. However, when the real parent and the ptracer are in the same thread group, only the ptrace stop event is visible through wait(2) and the duplicate notifications are different from the current behavior and are confusing. Suppress group stop notification in such cases. The continued state is shared between the real parent and the ptracer but is only meaningful to the real parent. Always notify the real parent and notify the ptracer too for backward compatibility. Similar to stop notification, if the real parent is the ptracer, suppress a duplicate notification. Test case follows. #include <stdio.h> #include <unistd.h> #include <time.h> #include <errno.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> int main(void) { const struct timespec ts100ms = { .tv_nsec = 100000000 }; pid_t tracee, tracer; siginfo_t si; int i; tracee = fork(); if (tracee == 0) { while (1) { printf("tracee: SIGSTOP\n"); raise(SIGSTOP); nanosleep(&ts100ms, NULL); printf("tracee: SIGCONT\n"); raise(SIGCONT); nanosleep(&ts100ms, NULL); } } waitid(P_PID, tracee, &si, WSTOPPED | WNOHANG | WNOWAIT); tracer = fork(); if (tracer == 0) { nanosleep(&ts100ms, NULL); ptrace(PTRACE_ATTACH, tracee, NULL, NULL); for (i = 0; i < 11; i++) { si.si_pid = 0; waitid(P_PID, tracee, &si, WSTOPPED); if (si.si_pid && si.si_code == CLD_TRAPPED) ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status); } printf("tracer: EXITING\n"); return 0; } while (1) { si.si_pid = 0; waitid(P_PID, tracee, &si, WSTOPPED | WCONTINUED | WEXITED); if (si.si_pid) printf("mommy : WAIT status=%02d code=%02d\n", si.si_status, si.si_code); } return 0; } Before this patch, while ptraced, the real parent doesn't get notifications for job control events, so although it can access those events, the later waitid(2) call never wakes up. tracee: SIGSTOP mommy : WAIT status=19 code=05 tracee: SIGCONT tracee: SIGSTOP tracee: SIGCONT tracee: SIGSTOP tracee: SIGCONT tracee: SIGSTOP tracer: EXITING mommy : WAIT status=19 code=05 ^C After this patch, it works as expected. tracee: SIGSTOP mommy : WAIT status=19 code=05 tracee: SIGCONT mommy : WAIT status=18 code=06 tracee: SIGSTOP mommy : WAIT status=19 code=05 tracee: SIGCONT mommy : WAIT status=18 code=06 tracee: SIGSTOP mommy : WAIT status=19 code=05 tracee: SIGCONT mommy : WAIT status=18 code=06 tracee: SIGSTOP tracer: EXITING mommy : WAIT status=19 code=05 ^C -v2: Oleg pointed out that * Group stop notification to the real parent should also happen when ptracer detach races with ptrace_stop(). * real_parent_is_ptracer() should be testing thread group equality not the task itself as wait(2) and stop/cont notifications are normally thread-group wide. Both issues are fixed accordingly. -v3: real_parent_is_ptracer() updated to test child->real_parent instead of child->group_leader->real_parent per Oleg's suggestion. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Oleg Nesterov <oleg@redhat.com>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r--kernel/signal.c53
1 files changed, 50 insertions, 3 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index 9f10b246fd46..f65403da4101 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1694,6 +1694,15 @@ static int sigkill_pending(struct task_struct *tsk)
1694} 1694}
1695 1695
1696/* 1696/*
1697 * Test whether the target task of the usual cldstop notification - the
1698 * real_parent of @child - is in the same group as the ptracer.
1699 */
1700static bool real_parent_is_ptracer(struct task_struct *child)
1701{
1702 return same_thread_group(child->parent, child->real_parent);
1703}
1704
1705/*
1697 * This must be called with current->sighand->siglock held. 1706 * This must be called with current->sighand->siglock held.
1698 * 1707 *
1699 * This should be the path for all ptrace stops. 1708 * This should be the path for all ptrace stops.
@@ -1708,6 +1717,8 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
1708 __releases(&current->sighand->siglock) 1717 __releases(&current->sighand->siglock)
1709 __acquires(&current->sighand->siglock) 1718 __acquires(&current->sighand->siglock)
1710{ 1719{
1720 bool gstop_done = false;
1721
1711 if (arch_ptrace_stop_needed(exit_code, info)) { 1722 if (arch_ptrace_stop_needed(exit_code, info)) {
1712 /* 1723 /*
1713 * The arch code has something special to do before a 1724 * The arch code has something special to do before a
@@ -1735,7 +1746,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
1735 * is entered - ignore it. 1746 * is entered - ignore it.
1736 */ 1747 */
1737 if (why == CLD_STOPPED && (current->group_stop & GROUP_STOP_PENDING)) 1748 if (why == CLD_STOPPED && (current->group_stop & GROUP_STOP_PENDING))
1738 task_participate_group_stop(current); 1749 gstop_done = task_participate_group_stop(current);
1739 1750
1740 current->last_siginfo = info; 1751 current->last_siginfo = info;
1741 current->exit_code = exit_code; 1752 current->exit_code = exit_code;
@@ -1757,7 +1768,20 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
1757 spin_unlock_irq(&current->sighand->siglock); 1768 spin_unlock_irq(&current->sighand->siglock);
1758 read_lock(&tasklist_lock); 1769 read_lock(&tasklist_lock);
1759 if (may_ptrace_stop()) { 1770 if (may_ptrace_stop()) {
1760 do_notify_parent_cldstop(current, task_ptrace(current), why); 1771 /*
1772 * Notify parents of the stop.
1773 *
1774 * While ptraced, there are two parents - the ptracer and
1775 * the real_parent of the group_leader. The ptracer should
1776 * know about every stop while the real parent is only
1777 * interested in the completion of group stop. The states
1778 * for the two don't interact with each other. Notify
1779 * separately unless they're gonna be duplicates.
1780 */
1781 do_notify_parent_cldstop(current, true, why);
1782 if (gstop_done && !real_parent_is_ptracer(current))
1783 do_notify_parent_cldstop(current, false, why);
1784
1761 /* 1785 /*
1762 * Don't want to allow preemption here, because 1786 * Don't want to allow preemption here, because
1763 * sys_ptrace() needs this task to be inactive. 1787 * sys_ptrace() needs this task to be inactive.
@@ -1772,7 +1796,16 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
1772 /* 1796 /*
1773 * By the time we got the lock, our tracer went away. 1797 * By the time we got the lock, our tracer went away.
1774 * Don't drop the lock yet, another tracer may come. 1798 * Don't drop the lock yet, another tracer may come.
1799 *
1800 * If @gstop_done, the ptracer went away between group stop
1801 * completion and here. During detach, it would have set
1802 * GROUP_STOP_PENDING on us and we'll re-enter TASK_STOPPED
1803 * in do_signal_stop() on return, so notifying the real
1804 * parent of the group stop completion is enough.
1775 */ 1805 */
1806 if (gstop_done)
1807 do_notify_parent_cldstop(current, false, why);
1808
1776 __set_current_state(TASK_RUNNING); 1809 __set_current_state(TASK_RUNNING);
1777 if (clear_code) 1810 if (clear_code)
1778 current->exit_code = 0; 1811 current->exit_code = 0;
@@ -2017,10 +2050,24 @@ relock:
2017 2050
2018 spin_unlock_irq(&sighand->siglock); 2051 spin_unlock_irq(&sighand->siglock);
2019 2052
2053 /*
2054 * Notify the parent that we're continuing. This event is
2055 * always per-process and doesn't make whole lot of sense
2056 * for ptracers, who shouldn't consume the state via
2057 * wait(2) either, but, for backward compatibility, notify
2058 * the ptracer of the group leader too unless it's gonna be
2059 * a duplicate.
2060 */
2020 read_lock(&tasklist_lock); 2061 read_lock(&tasklist_lock);
2062
2063 do_notify_parent_cldstop(current, false, why);
2064
2021 leader = current->group_leader; 2065 leader = current->group_leader;
2022 do_notify_parent_cldstop(leader, task_ptrace(leader), why); 2066 if (task_ptrace(leader) && !real_parent_is_ptracer(leader))
2067 do_notify_parent_cldstop(leader, true, why);
2068
2023 read_unlock(&tasklist_lock); 2069 read_unlock(&tasklist_lock);
2070
2024 goto relock; 2071 goto relock;
2025 } 2072 }
2026 2073