• T
    ptrace: implement PTRACE_INTERRUPT · fca26f26
    Tejun Heo 提交于
    Currently, there's no way to trap a running ptracee short of sending a
    signal which has various side effects.  This patch implements
    PTRACE_INTERRUPT which traps ptracee without any signal or job control
    related side effect.
    
    The implementation is almost trivial.  It uses the group stop trap -
    SIGTRAP | PTRACE_EVENT_STOP << 8.  A new trap flag
    JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
    cleared when any trap happens.  As INTERRUPT should be useable
    regardless of the current state of tracee, task_is_traced() test in
    ptrace_check_attach() is skipped for INTERRUPT.
    
    PTRACE_INTERRUPT is available iff tracee is attached with
    PTRACE_SEIZE.
    
    Test program follows.
    
      #define PTRACE_SEIZE		0x4206
      #define PTRACE_INTERRUPT	0x4207
    
      #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 pid=%d\n", getpid());
    			  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: INTERRUPT and DETACH\n");
    	  ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
    	  waitid(P_PID, tracee, NULL, WSTOPPED);
    	  ptrace(PTRACE_DETACH, tracee, NULL, NULL);
    	  nanosleep(&ts3s, NULL);
    
    	  printf("tracer: exiting\n");
    	  kill(tracee, SIGKILL);
    	  return 0;
      }
    
    When called without argument, tracee is seized from running state,
    interrupted and then detached back to running state.
    
      # ./test-interrupt
      tracee: alive pid=4546
      tracee: alive pid=4546
      tracee: alive pid=4546
      tracer: INTERRUPT and DETACH
      tracee: alive pid=4546
      tracee: alive pid=4546
      tracee: alive pid=4546
      tracer: exiting
    
    When called with argument, tracee is seized from stopped state,
    continued, interrupted and then detached back to stopped state.
    
      # ./test-interrupt  1
      tracee: alive pid=4548
      tracee: alive pid=4548
      tracee: alive pid=4548
      tracer: INTERRUPT and DETACH
      tracer: exiting
    
    Before PTRACE_INTERRUPT, once the tracee was running, there was no way
    to trap tracee and do PTRACE_DETACH without causing side effect.
    
    -v2: Updated to use task_set_jobctl_pending() so that it doesn't end
         up scheduling TRAP_STOP if child is dying which may make the
         child unkillable.  Spotted by Oleg.
    Signed-off-by: NTejun Heo <tj@kernel.org>
    Cc: Oleg Nesterov <oleg@redhat.com>
    fca26f26
ptrace.h 13.1 KB