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
commitd79fdd6d96f46fabb779d86332e3677c6f5c2a4f (patch)
tree2797e34888c687b47997b7c7ea3150468bcbb737 /kernel/signal.c
parent5224fa3660ad3881d2f2ad726d22614117963f10 (diff)
ptrace: Clean transitions between TASK_STOPPED and TRACED
Currently, if the task is STOPPED on ptrace attach, it's left alone and the state is silently changed to TRACED on the next ptrace call. The behavior breaks the assumption that arch_ptrace_stop() is called before any task is poked by ptrace and is ugly in that a task manipulates the state of another task directly. With GROUP_STOP_PENDING, the transitions between TASK_STOPPED and TRACED can be made clean. The tracer can use the flag to tell the tracee to retry stop on attach and detach. On retry, the tracee will enter the desired state in the correct way. The lower 16bits of task->group_stop is used to remember the signal number which caused the last group stop. This is used while retrying for ptrace attach as the original group_exit_code could have been consumed with wait(2) by then. As the real parent may wait(2) and consume the group_exit_code anytime, the group_exit_code needs to be saved separately so that it can be used when switching from regular sleep to ptrace_stop(). This is recorded in the lower 16bits of task->group_stop. If a task is already stopped and there's no intervening SIGCONT, a ptrace request immediately following a successful PTRACE_ATTACH should always succeed even if the tracer doesn't wait(2) for attach completion; however, with this change, the tracee might still be TASK_RUNNING trying to enter TASK_TRACED which would cause the following request to fail with -ESRCH. This intermediate state is hidden from the ptracer by setting GROUP_STOP_TRAPPING on attach and making ptrace_check_attach() wait for it to clear on its signal->wait_chldexit. Completing the transition or getting killed clears TRAPPING and wakes up the tracer. Note that the STOPPED -> RUNNING -> TRACED transition is still visible to other threads which are in the same group as the ptracer and the reverse transition is visible to all. Please read the comments for details. Oleg: * Spotted a race condition where a task may retry group stop without proper bookkeeping. Fixed by redoing bookkeeping on retry. * Spotted that the transition is visible to userland in several different ways. Most are fixed with GROUP_STOP_TRAPPING. Unhandled corner case is documented. * Pointed out not setting GROUP_STOP_SIGMASK on an already stopped task would result in more consistent behavior. * Pointed out that calling ptrace_stop() from do_signal_stop() in TASK_STOPPED can race with group stop start logic and then confuse the TRAPPING wait in ptrace_check_attach(). ptrace_stop() is now called with TASK_RUNNING. * Suggested using signal->wait_chldexit instead of bit wait. * Spotted a race condition between TRACED transition and clearing of TRAPPING. 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.c79
1 files changed, 66 insertions, 13 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index 418776c41d24..1e199919ae09 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -224,6 +224,26 @@ static inline void print_dropped_signal(int sig)
224} 224}
225 225
226/** 226/**
227 * task_clear_group_stop_trapping - clear group stop trapping bit
228 * @task: target task
229 *
230 * If GROUP_STOP_TRAPPING is set, a ptracer is waiting for us. Clear it
231 * and wake up the ptracer. Note that we don't need any further locking.
232 * @task->siglock guarantees that @task->parent points to the ptracer.
233 *
234 * CONTEXT:
235 * Must be called with @task->sighand->siglock held.
236 */
237static void task_clear_group_stop_trapping(struct task_struct *task)
238{
239 if (unlikely(task->group_stop & GROUP_STOP_TRAPPING)) {
240 task->group_stop &= ~GROUP_STOP_TRAPPING;
241 __wake_up_sync(&task->parent->signal->wait_chldexit,
242 TASK_UNINTERRUPTIBLE, 1);
243 }
244}
245
246/**
227 * task_clear_group_stop_pending - clear pending group stop 247 * task_clear_group_stop_pending - clear pending group stop
228 * @task: target task 248 * @task: target task
229 * 249 *
@@ -1706,8 +1726,20 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
1706 current->last_siginfo = info; 1726 current->last_siginfo = info;
1707 current->exit_code = exit_code; 1727 current->exit_code = exit_code;
1708 1728
1709 /* Let the debugger run. */ 1729 /*
1710 __set_current_state(TASK_TRACED); 1730 * TRACED should be visible before TRAPPING is cleared; otherwise,
1731 * the tracer might fail do_wait().
1732 */
1733 set_current_state(TASK_TRACED);
1734
1735 /*
1736 * We're committing to trapping. Clearing GROUP_STOP_TRAPPING and
1737 * transition to TASK_TRACED should be atomic with respect to
1738 * siglock. This hsould be done after the arch hook as siglock is
1739 * released and regrabbed across it.
1740 */
1741 task_clear_group_stop_trapping(current);
1742
1711 spin_unlock_irq(&current->sighand->siglock); 1743 spin_unlock_irq(&current->sighand->siglock);
1712 read_lock(&tasklist_lock); 1744 read_lock(&tasklist_lock);
1713 if (may_ptrace_stop()) { 1745 if (may_ptrace_stop()) {
@@ -1788,6 +1820,9 @@ static int do_signal_stop(int signr)
1788 unsigned int gstop = GROUP_STOP_PENDING | GROUP_STOP_CONSUME; 1820 unsigned int gstop = GROUP_STOP_PENDING | GROUP_STOP_CONSUME;
1789 struct task_struct *t; 1821 struct task_struct *t;
1790 1822
1823 /* signr will be recorded in task->group_stop for retries */
1824 WARN_ON_ONCE(signr & ~GROUP_STOP_SIGMASK);
1825
1791 if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) || 1826 if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) ||
1792 unlikely(signal_group_exit(sig))) 1827 unlikely(signal_group_exit(sig)))
1793 return 0; 1828 return 0;
@@ -1797,25 +1832,27 @@ static int do_signal_stop(int signr)
1797 */ 1832 */
1798 sig->group_exit_code = signr; 1833 sig->group_exit_code = signr;
1799 1834
1800 current->group_stop = gstop; 1835 current->group_stop &= ~GROUP_STOP_SIGMASK;
1836 current->group_stop |= signr | gstop;
1801 sig->group_stop_count = 1; 1837 sig->group_stop_count = 1;
1802 for (t = next_thread(current); t != current; t = next_thread(t)) 1838 for (t = next_thread(current); t != current;
1839 t = next_thread(t)) {
1840 t->group_stop &= ~GROUP_STOP_SIGMASK;
1803 /* 1841 /*
1804 * Setting state to TASK_STOPPED for a group 1842 * Setting state to TASK_STOPPED for a group
1805 * stop is always done with the siglock held, 1843 * stop is always done with the siglock held,
1806 * so this check has no races. 1844 * so this check has no races.
1807 */ 1845 */
1808 if (!(t->flags & PF_EXITING) && !task_is_stopped(t)) { 1846 if (!(t->flags & PF_EXITING) && !task_is_stopped(t)) {
1809 t->group_stop = gstop; 1847 t->group_stop |= signr | gstop;
1810 sig->group_stop_count++; 1848 sig->group_stop_count++;
1811 signal_wake_up(t, 0); 1849 signal_wake_up(t, 0);
1812 } else 1850 } else {
1813 task_clear_group_stop_pending(t); 1851 task_clear_group_stop_pending(t);
1852 }
1853 }
1814 } 1854 }
1815 1855retry:
1816 current->exit_code = sig->group_exit_code;
1817 __set_current_state(TASK_STOPPED);
1818
1819 if (likely(!task_ptrace(current))) { 1856 if (likely(!task_ptrace(current))) {
1820 int notify = 0; 1857 int notify = 0;
1821 1858
@@ -1827,6 +1864,7 @@ static int do_signal_stop(int signr)
1827 if (task_participate_group_stop(current)) 1864 if (task_participate_group_stop(current))
1828 notify = CLD_STOPPED; 1865 notify = CLD_STOPPED;
1829 1866
1867 __set_current_state(TASK_STOPPED);
1830 spin_unlock_irq(&current->sighand->siglock); 1868 spin_unlock_irq(&current->sighand->siglock);
1831 1869
1832 if (notify) { 1870 if (notify) {
@@ -1839,13 +1877,28 @@ static int do_signal_stop(int signr)
1839 schedule(); 1877 schedule();
1840 1878
1841 spin_lock_irq(&current->sighand->siglock); 1879 spin_lock_irq(&current->sighand->siglock);
1842 } else 1880 } else {
1843 ptrace_stop(current->exit_code, CLD_STOPPED, 0, NULL); 1881 ptrace_stop(current->group_stop & GROUP_STOP_SIGMASK,
1882 CLD_STOPPED, 0, NULL);
1883 current->exit_code = 0;
1884 }
1885
1886 /*
1887 * GROUP_STOP_PENDING could be set if another group stop has
1888 * started since being woken up or ptrace wants us to transit
1889 * between TASK_STOPPED and TRACED. Retry group stop.
1890 */
1891 if (current->group_stop & GROUP_STOP_PENDING) {
1892 WARN_ON_ONCE(!(current->group_stop & GROUP_STOP_SIGMASK));
1893 goto retry;
1894 }
1895
1896 /* PTRACE_ATTACH might have raced with task killing, clear trapping */
1897 task_clear_group_stop_trapping(current);
1844 1898
1845 spin_unlock_irq(&current->sighand->siglock); 1899 spin_unlock_irq(&current->sighand->siglock);
1846 1900
1847 tracehook_finish_jctl(); 1901 tracehook_finish_jctl();
1848 current->exit_code = 0;
1849 1902
1850 return 1; 1903 return 1;
1851} 1904}