• Tejun Heo's avatar
    signal: Fix premature completion of group stop when interfered by ptrace · e5c1902e
    Tejun Heo authored
    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: default avatarTejun Heo <tj@kernel.org>
    Acked-by: default avatarOleg Nesterov <oleg@redhat.com>
    Cc: Roland McGrath <roland@redhat.com>
    e5c1902e
signal.c 70.7 KB