aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/sched.h
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
commite5c1902e9260a0075ea52cb5ef627a8d9aaede89 (patch)
tree0fc5ec2460fecfe564323d89227bec9293238c2a /include/linux/sched.h
parentfe1bc6a0954611b806f9e158eb0817cf8ba21660 (diff)
signal: Fix premature completion of group stop when interfered by ptrace
task->signal->group_stop_count is used to track the progress of group stop. It's initialized to the number of tasks which need to stop for group stop to finish and each stopping or trapping task decrements. However, each task doesn't keep track of whether it decremented the counter or not and if woken up before the group stop is complete and stops again, it can decrement the counter multiple times. Please consider the following example code. static void *worker(void *arg) { while (1) ; return NULL; } int main(void) { pthread_t thread; pid_t pid; int i; pid = fork(); if (!pid) { for (i = 0; i < 5; i++) pthread_create(&thread, NULL, worker, NULL); while (1) ; return 0; } ptrace(PTRACE_ATTACH, pid, NULL, NULL); while (1) { waitid(P_PID, pid, NULL, WSTOPPED); ptrace(PTRACE_SINGLESTEP, pid, NULL, (void *)(long)SIGSTOP); } return 0; } The child creates five threads and the parent continuously traps the first thread and whenever the child gets a signal, SIGSTOP is delivered. If an external process sends SIGSTOP to the child, all other threads in the process should reliably stop. However, due to the above bug, the first thread will often end up consuming group_stop_count multiple times and SIGSTOP often ends up stopping none or part of the other four threads. This patch adds a new field task->group_stop which is protected by siglock and uses GROUP_STOP_CONSUME flag to track which task is still to consume group_stop_count to fix this bug. task_clear_group_stop_pending() and task_participate_group_stop() are added to help manipulating group stop states. As ptrace_stop() now also uses task_participate_group_stop(), it will set SIGNAL_STOP_STOPPED if it completes a group stop. There still are many issues regarding the interaction between group stop and ptrace. Patches to address them will follow. - Oleg spotted duplicate GROUP_STOP_CONSUME. Dropped. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Oleg Nesterov <oleg@redhat.com> Cc: Roland McGrath <roland@redhat.com>
Diffstat (limited to 'include/linux/sched.h')
-rw-r--r--include/linux/sched.h6
1 files changed, 6 insertions, 0 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 4b601be3dace..85f51042c2b8 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1260,6 +1260,7 @@ struct task_struct {
1260 int exit_state; 1260 int exit_state;
1261 int exit_code, exit_signal; 1261 int exit_code, exit_signal;
1262 int pdeath_signal; /* The signal sent when the parent dies */ 1262 int pdeath_signal; /* The signal sent when the parent dies */
1263 unsigned int group_stop; /* GROUP_STOP_*, siglock protected */
1263 /* ??? */ 1264 /* ??? */
1264 unsigned int personality; 1265 unsigned int personality;
1265 unsigned did_exec:1; 1266 unsigned did_exec:1;
@@ -1771,6 +1772,11 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *
1771#define tsk_used_math(p) ((p)->flags & PF_USED_MATH) 1772#define tsk_used_math(p) ((p)->flags & PF_USED_MATH)
1772#define used_math() tsk_used_math(current) 1773#define used_math() tsk_used_math(current)
1773 1774
1775/*
1776 * task->group_stop flags
1777 */
1778#define GROUP_STOP_CONSUME (1 << 17) /* consume group stop count */
1779
1774#ifdef CONFIG_PREEMPT_RCU 1780#ifdef CONFIG_PREEMPT_RCU
1775 1781
1776#define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */ 1782#define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */