• Tejun Heo's avatar
    ptrace: implement PTRACE_SEIZE · 3544d72a
    Tejun Heo authored
    PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
    effects on tracee signal and job control states.  This patch
    implements a new ptrace request PTRACE_SEIZE which attaches a tracee
    without trapping it or affecting its signal and job control states.
    
    The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
    flags in @data.  Currently, the only defined flag is
    PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
    PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
    The changes will be implemented gradually and the DEVEL flag is to
    prevent programs which expect full SEIZE behavior from using it before
    all the behavior modifications are complete while allowing unit
    testing.  The flag will be removed once SEIZE behaviors are completely
    implemented.
    
    * PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap.  After
      attaching tracee continues to run unless a trap condition occurs.
    
    * PTRACE_SEIZE doesn't affect signal or group stop state.
    
    * If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
      exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
      the stopping signals if group stop is in effect or SIGTRAP
      otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
      instead of NULL.
    
    Seizing sets PT_SEIZED in ->ptrace of the tracee.  This flag will be
    used to determine whether new SEIZE behaviors should be enabled.
    
    Test program follows.
    
      #define PTRACE_SEIZE		0x4206
      #define PTRACE_SEIZE_DEVEL	0x80000000
    
      static const struct timespec ts100ms = { .tv_nsec = 100000000 };
      static const struct timespec ts1s = { .tv_sec = 1 };
      static const struct timespec ts3s = { .tv_sec = 3 };
    
      int main(int argc, char **argv)
      {
    	  pid_t tracee;
    
    	  tracee = fork();
    	  if (tracee == 0) {
    		  nanosleep(&ts100ms, NULL);
    		  while (1) {
    			  printf("tracee: alive\n");
    			  nanosleep(&ts1s, NULL);
    		  }
    	  }
    
    	  if (argc > 1)
    		  kill(tracee, SIGSTOP);
    
    	  nanosleep(&ts100ms, NULL);
    
    	  ptrace(PTRACE_SEIZE, tracee, NULL,
    		 (void *)(unsigned long)PTRACE_SEIZE_DEVEL);
    	  if (argc > 1) {
    		  waitid(P_PID, tracee, NULL, WSTOPPED);
    		  ptrace(PTRACE_CONT, tracee, NULL, NULL);
    	  }
    	  nanosleep(&ts3s, NULL);
    	  printf("tracer: exiting\n");
    	  return 0;
      }
    
    When the above program is called w/o argument, tracee is seized while
    running and remains running.  When tracer exits, tracee continues to
    run and print out messages.
    
      # ./test-seize-simple
      tracee: alive
      tracee: alive
      tracee: alive
      tracer: exiting
      tracee: alive
      tracee: alive
    
    When called with an argument, tracee is seized from stopped state and
    continued, and returns to stopped state when tracer exits.
    
      # ./test-seize
      tracee: alive
      tracee: alive
      tracee: alive
      tracer: exiting
      # ps -el|grep test-seize
      1 T     0  4720     1  0  80   0 -   941 signal ttyS0    00:00:00 test-seize
    
    -v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
         suggested.
    
    -v3: PTRACE_EVENT_STOP traps now report group stop state by signr.  If
         group stop is in effect the stop signal number is returned as
         part of exit_code; otherwise, SIGTRAP.  This was suggested by
         Denys and Oleg.
    Signed-off-by: default avatarTejun Heo <tj@kernel.org>
    Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
    Cc: Denys Vlasenko <vda.linux@googlemail.com>
    Cc: Oleg Nesterov <oleg@redhat.com>
    3544d72a
signal.c 81.5 KB