• T
    job control: Don't send duplicate job control stop notification while ptraced · 244056f9
    Tejun Heo 提交于
    Just as group_exit_code shouldn't be generated when a PTRACE_CONT'd
    task re-enters job control stop, notifiction for the event should be
    suppressed too.  The logic is the same as the group_exit_code
    generation suppression in do_signal_stop(), if SIGNAL_STOP_STOPPED is
    already set, the task is re-entering job control stop without
    intervening SIGCONT and the notifications should be suppressed.
    
    Test case follows.
    
      #include <stdio.h>
      #include <unistd.h>
      #include <signal.h>
      #include <time.h>
      #include <sys/ptrace.h>
      #include <sys/wait.h>
    
      static const struct timespec ts100ms = { .tv_nsec = 100000000 };
      static pid_t tracee, tracer;
    
      static const char *pid_who(pid_t pid)
      {
    	  return pid == tracee ? "tracee" : (pid == tracer ? "tracer" : "mommy ");
      }
    
      static void sigchld_sigaction(int signo, siginfo_t *si, void *ucxt)
      {
    	  printf("%s: SIG status=%02d code=%02d (%s)\n",
    		 pid_who(getpid()), si->si_status, si->si_code,
    		 pid_who(si->si_pid));
      }
    
      int main(void)
      {
    	  const struct sigaction chld_sa = { .sa_sigaction = sigchld_sigaction,
    					     .sa_flags = SA_SIGINFO|SA_RESTART };
    	  siginfo_t si;
    
    	  sigaction(SIGCHLD, &chld_sa, NULL);
    
    	  tracee = fork();
    	  if (!tracee) {
    		  tracee = getpid();
    		  while (1)
    			  pause();
    	  }
    
    	  kill(tracee, SIGSTOP);
    	  waitid(P_PID, tracee, &si, WSTOPPED);
    
    	  tracer = fork();
    	  if (!tracer) {
    		  tracer = getpid();
    		  ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
    		  waitid(P_PID, tracee, &si, WSTOPPED);
    		  ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
    		  waitid(P_PID, tracee, &si, WSTOPPED);
    		  ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
    		  waitid(P_PID, tracee, &si, WSTOPPED);
    		  printf("tracer: detaching\n");
    		  ptrace(PTRACE_DETACH, tracee, NULL, NULL);
    		  return 0;
    	  }
    
    	  while (1)
    		  pause();
    	  return 0;
      }
    
    Before the patch, the parent gets the second notification for the
    tracee after the tracer detaches.  si_status is zero because
    group_exit_code is not set by the group stop completion which
    triggered this notification.
    
      mommy : SIG status=19 code=05 (tracee)
      tracer: SIG status=00 code=05 (tracee)
      tracer: SIG status=19 code=04 (tracee)
      tracer: SIG status=00 code=05 (tracee)
      tracer: detaching
      mommy : SIG status=00 code=05 (tracee)
      mommy : SIG status=00 code=01 (tracer)
      ^C
    
    After the patch, the duplicate notification is gone.
    
      mommy : SIG status=19 code=05 (tracee)
      tracer: SIG status=00 code=05 (tracee)
      tracer: SIG status=19 code=04 (tracee)
      tracer: SIG status=00 code=05 (tracee)
      tracer: detaching
      mommy : SIG status=00 code=01 (tracer)
      ^C
    Signed-off-by: NTejun Heo <tj@kernel.org>
    Acked-by: NOleg Nesterov <oleg@redhat.com>
    244056f9
signal.c 76.6 KB