diff options
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | kernel/ptrace.c | 49 | ||||
-rw-r--r-- | kernel/signal.c | 79 |
3 files changed, 112 insertions, 18 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index b2a17dfbdbad..456d80ed3b78 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1775,8 +1775,10 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * | |||
1775 | /* | 1775 | /* |
1776 | * task->group_stop flags | 1776 | * task->group_stop flags |
1777 | */ | 1777 | */ |
1778 | #define GROUP_STOP_SIGMASK 0xffff /* signr of the last group stop */ | ||
1778 | #define GROUP_STOP_PENDING (1 << 16) /* task should stop for group stop */ | 1779 | #define GROUP_STOP_PENDING (1 << 16) /* task should stop for group stop */ |
1779 | #define GROUP_STOP_CONSUME (1 << 17) /* consume group stop count */ | 1780 | #define GROUP_STOP_CONSUME (1 << 17) /* consume group stop count */ |
1781 | #define GROUP_STOP_TRAPPING (1 << 18) /* switching from STOPPED to TRACED */ | ||
1780 | 1782 | ||
1781 | extern void task_clear_group_stop_pending(struct task_struct *task); | 1783 | extern void task_clear_group_stop_pending(struct task_struct *task); |
1782 | 1784 | ||
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 6acf8954017c..745fc2dd00c5 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -49,14 +49,22 @@ static void ptrace_untrace(struct task_struct *child) | |||
49 | spin_lock(&child->sighand->siglock); | 49 | spin_lock(&child->sighand->siglock); |
50 | if (task_is_traced(child)) { | 50 | if (task_is_traced(child)) { |
51 | /* | 51 | /* |
52 | * If the group stop is completed or in progress, | 52 | * If group stop is completed or in progress, it should |
53 | * this thread was already counted as stopped. | 53 | * participate in the group stop. Set GROUP_STOP_PENDING |
54 | * before kicking it. | ||
55 | * | ||
56 | * This involves TRACED -> RUNNING -> STOPPED transition | ||
57 | * which is similar to but in the opposite direction of | ||
58 | * what happens while attaching to a stopped task. | ||
59 | * However, in this direction, the intermediate RUNNING | ||
60 | * state is not hidden even from the current ptracer and if | ||
61 | * it immediately re-attaches and performs a WNOHANG | ||
62 | * wait(2), it may fail. | ||
54 | */ | 63 | */ |
55 | if (child->signal->flags & SIGNAL_STOP_STOPPED || | 64 | if (child->signal->flags & SIGNAL_STOP_STOPPED || |
56 | child->signal->group_stop_count) | 65 | child->signal->group_stop_count) |
57 | __set_task_state(child, TASK_STOPPED); | 66 | child->group_stop |= GROUP_STOP_PENDING; |
58 | else | 67 | signal_wake_up(child, 1); |
59 | signal_wake_up(child, 1); | ||
60 | } | 68 | } |
61 | spin_unlock(&child->sighand->siglock); | 69 | spin_unlock(&child->sighand->siglock); |
62 | } | 70 | } |
@@ -165,6 +173,7 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
165 | 173 | ||
166 | static int ptrace_attach(struct task_struct *task) | 174 | static int ptrace_attach(struct task_struct *task) |
167 | { | 175 | { |
176 | bool wait_trap = false; | ||
168 | int retval; | 177 | int retval; |
169 | 178 | ||
170 | audit_ptrace(task); | 179 | audit_ptrace(task); |
@@ -204,12 +213,42 @@ static int ptrace_attach(struct task_struct *task) | |||
204 | __ptrace_link(task, current); | 213 | __ptrace_link(task, current); |
205 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); | 214 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); |
206 | 215 | ||
216 | spin_lock(&task->sighand->siglock); | ||
217 | |||
218 | /* | ||
219 | * If the task is already STOPPED, set GROUP_STOP_PENDING and | ||
220 | * TRAPPING, and kick it so that it transits to TRACED. TRAPPING | ||
221 | * will be cleared if the child completes the transition or any | ||
222 | * event which clears the group stop states happens. We'll wait | ||
223 | * for the transition to complete before returning from this | ||
224 | * function. | ||
225 | * | ||
226 | * This hides STOPPED -> RUNNING -> TRACED transition from the | ||
227 | * attaching thread but a different thread in the same group can | ||
228 | * still observe the transient RUNNING state. IOW, if another | ||
229 | * thread's WNOHANG wait(2) on the stopped tracee races against | ||
230 | * ATTACH, the wait(2) may fail due to the transient RUNNING. | ||
231 | * | ||
232 | * The following task_is_stopped() test is safe as both transitions | ||
233 | * in and out of STOPPED are protected by siglock. | ||
234 | */ | ||
235 | if (task_is_stopped(task)) { | ||
236 | task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING; | ||
237 | signal_wake_up(task, 1); | ||
238 | wait_trap = true; | ||
239 | } | ||
240 | |||
241 | spin_unlock(&task->sighand->siglock); | ||
242 | |||
207 | retval = 0; | 243 | retval = 0; |
208 | unlock_tasklist: | 244 | unlock_tasklist: |
209 | write_unlock_irq(&tasklist_lock); | 245 | write_unlock_irq(&tasklist_lock); |
210 | unlock_creds: | 246 | unlock_creds: |
211 | mutex_unlock(&task->signal->cred_guard_mutex); | 247 | mutex_unlock(&task->signal->cred_guard_mutex); |
212 | out: | 248 | out: |
249 | if (wait_trap) | ||
250 | wait_event(current->signal->wait_chldexit, | ||
251 | !(task->group_stop & GROUP_STOP_TRAPPING)); | ||
213 | return retval; | 252 | return retval; |
214 | } | 253 | } |
215 | 254 | ||
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 | */ | ||
237 | static 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(¤t->sighand->siglock); | 1743 | spin_unlock_irq(¤t->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 | 1855 | retry: | |
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(¤t->sighand->siglock); | 1868 | spin_unlock_irq(¤t->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(¤t->sighand->siglock); | 1879 | spin_lock_irq(¤t->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(¤t->sighand->siglock); | 1899 | spin_unlock_irq(¤t->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 | } |