ptrace: implement PTRACE_SEIZE
authorTejun Heo <tj@kernel.org>
Tue, 14 Jun 2011 09:20:15 +0000 (11:20 +0200)
committerOleg Nesterov <oleg@redhat.com>
Thu, 16 Jun 2011 19:41:53 +0000 (21:41 +0200)
commit3544d72a0e10d0aa1c1bd59ed77a53a59cdc12f7
tree27eb767b5cb98f58cccb5b7053a58bccd105f9d0
parent73ddff2bee159ffb580bd24faf625cd5e628f5ec
ptrace: implement PTRACE_SEIZE

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: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
include/linux/ptrace.h
kernel/ptrace.c
kernel/signal.c