1. 23 6月, 2011 1 次提交
  2. 17 6月, 2011 4 次提交
    • T
      ptrace: implement PTRACE_LISTEN · 544b2c91
      Tejun Heo 提交于
      The previous patch implemented async notification for ptrace but it
      only worked while trace is running.  This patch introduces
      PTRACE_LISTEN which is suggested by Oleg Nestrov.
      
      It's allowed iff tracee is in STOP trap and puts tracee into
      quasi-running state - tracee never really runs but wait(2) and
      ptrace(2) consider it to be running.  While ptracer is listening,
      tracee is allowed to re-enter STOP to notify an async event.
      Listening state is cleared on the first notification.  Ptracer can
      also clear it by issuing INTERRUPT - tracee will re-trap into STOP
      with listening state cleared.
      
      This allows ptracer to monitor group stop state without running tracee
      - use INTERRUPT to put tracee into STOP trap, issue LISTEN and then
      wait(2) to wait for the next group stop event.  When it happens,
      PTRACE_GETSIGINFO provides information to determine the current state.
      
      Test program follows.
      
        #define PTRACE_SEIZE		0x4206
        #define PTRACE_INTERRUPT	0x4207
        #define PTRACE_LISTEN		0x4208
      
        #define PTRACE_SEIZE_DEVEL	0x80000000
      
        static const struct timespec ts1s = { .tv_sec = 1 };
      
        int main(int argc, char **argv)
        {
      	  pid_t tracee, tracer;
      	  int i;
      
      	  tracee = fork();
      	  if (!tracee)
      		  while (1)
      			  pause();
      
      	  tracer = fork();
      	  if (!tracer) {
      		  siginfo_t si;
      
      		  ptrace(PTRACE_SEIZE, tracee, NULL,
      			 (void *)(unsigned long)PTRACE_SEIZE_DEVEL);
      		  ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
      	  repeat:
      		  waitid(P_PID, tracee, NULL, WSTOPPED);
      
      		  ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si);
      		  if (!si.si_code) {
      			  printf("tracer: SIG %d\n", si.si_signo);
      			  ptrace(PTRACE_CONT, tracee, NULL,
      				 (void *)(unsigned long)si.si_signo);
      			  goto repeat;
      		  }
      		  printf("tracer: stopped=%d signo=%d\n",
      			 si.si_signo != SIGTRAP, si.si_signo);
      		  if (si.si_signo != SIGTRAP)
      			  ptrace(PTRACE_LISTEN, tracee, NULL, NULL);
      		  else
      			  ptrace(PTRACE_CONT, tracee, NULL, NULL);
      		  goto repeat;
      	  }
      
      	  for (i = 0; i < 3; i++) {
      		  nanosleep(&ts1s, NULL);
      		  printf("mother: SIGSTOP\n");
      		  kill(tracee, SIGSTOP);
      		  nanosleep(&ts1s, NULL);
      		  printf("mother: SIGCONT\n");
      		  kill(tracee, SIGCONT);
      	  }
      	  nanosleep(&ts1s, NULL);
      
      	  kill(tracer, SIGKILL);
      	  kill(tracee, SIGKILL);
      	  return 0;
        }
      
      This is identical to the program to test TRAP_NOTIFY except that
      tracee is PTRACE_LISTEN'd instead of PTRACE_CONT'd when group stopped.
      This allows ptracer to monitor when group stop ends without running
      tracee.
      
        # ./test-listen
        tracer: stopped=0 signo=5
        mother: SIGSTOP
        tracer: SIG 19
        tracer: stopped=1 signo=19
        mother: SIGCONT
        tracer: stopped=0 signo=5
        tracer: SIG 18
        mother: SIGSTOP
        tracer: SIG 19
        tracer: stopped=1 signo=19
        mother: SIGCONT
        tracer: stopped=0 signo=5
        tracer: SIG 18
        mother: SIGSTOP
        tracer: SIG 19
        tracer: stopped=1 signo=19
        mother: SIGCONT
        tracer: stopped=0 signo=5
        tracer: SIG 18
      
      -v2: Moved JOBCTL_LISTENING check in wait_task_stopped() into
           task_stopped_code() as suggested by Oleg.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Cc: Oleg Nesterov <oleg@redhat.com>
      544b2c91
    • T
      ptrace: implement TRAP_NOTIFY and use it for group stop events · fb1d910c
      Tejun Heo 提交于
      Currently there's no way for ptracer to find out whether group stop
      finished other than polling with INTERRUPT - GETSIGINFO - CONT
      sequence.  This patch implements group stop notification for ptracer
      using STOP traps.
      
      When group stop state of a seized tracee changes, JOBCTL_TRAP_NOTIFY
      is set, which schedules a STOP trap which is sticky - it isn't cleared
      by other traps and at least one STOP trap will happen eventually.
      STOP trap is synchronization point for event notification and the
      tracer can determine the current group stop state by looking at the
      signal number portion of exit code (si_status from waitid(2) or
      si_code from PTRACE_GETSIGINFO).
      
      Notifications are generated both on start and end of group stops but,
      because group stop participation always happens before STOP trap, this
      doesn't cause an extra trap while tracee is participating in group
      stop.  The symmetry will be useful later.
      
      Note that this notification works iff tracee is not trapped.
      Currently there is no way to be notified of group stop state changes
      while tracee is trapped.  This will be addressed by a later patch.
      
      An example program follows.
      
        #define PTRACE_SEIZE		0x4206
        #define PTRACE_INTERRUPT	0x4207
      
        #define PTRACE_SEIZE_DEVEL	0x80000000
      
        static const struct timespec ts1s = { .tv_sec = 1 };
      
        int main(int argc, char **argv)
        {
      	  pid_t tracee, tracer;
      	  int i;
      
      	  tracee = fork();
      	  if (!tracee)
      		  while (1)
      			  pause();
      
      	  tracer = fork();
      	  if (!tracer) {
      		  siginfo_t si;
      
      		  ptrace(PTRACE_SEIZE, tracee, NULL,
      			 (void *)(unsigned long)PTRACE_SEIZE_DEVEL);
      		  ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
      	  repeat:
      		  waitid(P_PID, tracee, NULL, WSTOPPED);
      
      		  ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si);
      		  if (!si.si_code) {
      			  printf("tracer: SIG %d\n", si.si_signo);
      			  ptrace(PTRACE_CONT, tracee, NULL,
      				 (void *)(unsigned long)si.si_signo);
      			  goto repeat;
      		  }
      		  printf("tracer: stopped=%d signo=%d\n",
      			 si.si_signo != SIGTRAP, si.si_signo);
      		  ptrace(PTRACE_CONT, tracee, NULL, NULL);
      		  goto repeat;
      	  }
      
      	  for (i = 0; i < 3; i++) {
      		  nanosleep(&ts1s, NULL);
      		  printf("mother: SIGSTOP\n");
      		  kill(tracee, SIGSTOP);
      		  nanosleep(&ts1s, NULL);
      		  printf("mother: SIGCONT\n");
      		  kill(tracee, SIGCONT);
      	  }
      	  nanosleep(&ts1s, NULL);
      
      	  kill(tracer, SIGKILL);
      	  kill(tracee, SIGKILL);
      	  return 0;
        }
      
      In the above program, tracer keeps tracee running and gets
      notification of each group stop state changes.
      
        # ./test-notify
        tracer: stopped=0 signo=5
        mother: SIGSTOP
        tracer: SIG 19
        tracer: stopped=1 signo=19
        mother: SIGCONT
        tracer: stopped=0 signo=5
        tracer: SIG 18
        mother: SIGSTOP
        tracer: SIG 19
        tracer: stopped=1 signo=19
        mother: SIGCONT
        tracer: stopped=0 signo=5
        tracer: SIG 18
        mother: SIGSTOP
        tracer: SIG 19
        tracer: stopped=1 signo=19
        mother: SIGCONT
        tracer: stopped=0 signo=5
        tracer: SIG 18
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Cc: Oleg Nesterov <oleg@redhat.com>
      fb1d910c
    • T
      ptrace: implement PTRACE_SEIZE · 3544d72a
      Tejun Heo 提交于
      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: NTejun 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
    • T
      job control: introduce JOBCTL_TRAP_STOP and use it for group stop trap · 73ddff2b
      Tejun Heo 提交于
      do_signal_stop() implemented both normal group stop and trap for group
      stop while ptraced.  This approach has been enough but scheduled
      changes require trap mechanism which can be used in more generic
      manner and using group stop trap for generic trap site simplifies both
      userland visible interface and implementation.
      
      This patch adds a new jobctl flag - JOBCTL_TRAP_STOP.  When set, it
      triggers a trap site, which behaves like group stop trap, in
      get_signal_to_deliver() after checking for pending signals.  While
      ptraced, do_signal_stop() doesn't stop itself.  It initiates group
      stop if requested and schedules JOBCTL_TRAP_STOP and returns.  The
      caller - get_signal_to_deliver() - is responsible for checking whether
      TRAP_STOP is pending afterwards and handling it.
      
      ptrace_attach() is updated to use JOBCTL_TRAP_STOP instead of
      JOBCTL_STOP_PENDING and __ptrace_unlink() to clear all pending trap
      bits and TRAPPING so that TRAP_STOP and future trap bits don't linger
      after detach.
      
      While at it, add proper function comment to do_signal_stop() and make
      it return bool.
      
      -v2: __ptrace_unlink() updated to clear JOBCTL_TRAP_MASK and TRAPPING
           instead of JOBCTL_PENDING_MASK.  This avoids accidentally
           clearing JOBCTL_STOP_CONSUME.  Spotted by Oleg.
      
      -v3: do_signal_stop() updated to return %false without dropping
           siglock while ptraced and TRAP_STOP check moved inside for(;;)
           loop after group stop participation.  This avoids unnecessary
           relocking and also will help avoiding unnecessary traps by
           consuming group stop before handling pending traps.
      
      -v4: Jobctl trap handling moved into a separate function -
           do_jobctl_trap().
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Cc: Oleg Nesterov <oleg@redhat.com>
      73ddff2b
  3. 05 6月, 2011 7 次提交
    • T
      signal: remove three noop tracehooks · dd1d6772
      Tejun Heo 提交于
      Remove the following three noop tracehooks in signals.c.
      
      * tracehook_force_sigpending()
      * tracehook_get_signal()
      * tracehook_finish_jctl()
      
      The code area is about to be updated and these hooks don't do anything
      other than obfuscating the logic.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      dd1d6772
    • T
      ptrace: use bit_waitqueue for TRAPPING instead of wait_chldexit · 62c124ff
      Tejun Heo 提交于
      ptracer->signal->wait_chldexit was used to wait for TRAPPING; however,
      ->wait_chldexit was already complicated with waker-side filtering
      without adding TRAPPING wait on top of it.  Also, it unnecessarily
      made TRAPPING clearing depend on the current ptrace relationship - if
      the ptracee is detached, wakeup is lost.
      
      There is no reason to use signal->wait_chldexit here.  We're just
      waiting for JOBCTL_TRAPPING bit to clear and given the relatively
      infrequent use of ptrace, bit_waitqueue can serve it perfectly.
      
      This patch makes JOBCTL_TRAPPING wait use bit_waitqueue instead of
      signal->wait_chldexit.
      
      -v2: Use JOBCTL_*_BIT macros instead of ilog2() as suggested by Linus.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Cc: Linus Torvalds <torvalds@linux-foundation.org>
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      62c124ff
    • T
      job control: introduce task_set_jobctl_pending() · 7dd3db54
      Tejun Heo 提交于
      task->jobctl currently hosts JOBCTL_STOP_PENDING and will host TRAP
      pending bits too.  Setting pending conditions on a dying task may make
      the task unkillable.  Currently, each setting site is responsible for
      checking for the condition but with to-be-added job control traps this
      becomes too fragile.
      
      This patch adds task_set_jobctl_pending() which should be used when
      setting task->jobctl bits to schedule a stop or trap.  The function
      performs the followings to ease setting pending bits.
      
      * Sanity checks.
      
      * If fatal signal is pending or PF_EXITING is set, no bit is set.
      
      * STOP_SIGMASK is automatically cleared if new value is being set.
      
      do_signal_stop() and ptrace_attach() are updated to use
      task_set_jobctl_pending() instead of setting STOP_PENDING explicitly.
      The surrounding structures around setting are changed to fit
      task_set_jobctl_pending() better but there should be no userland
      visible behavior difference.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Cc: Oleg Nesterov <oleg@redhat.com>
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      7dd3db54
    • T
      job control: make task_clear_jobctl_pending() clear TRAPPING automatically · 6dfca329
      Tejun Heo 提交于
      JOBCTL_TRAPPING indicates that ptracer is waiting for tracee to
      (re)transit into TRACED.  task_clear_jobctl_pending() must be called
      when either tracee enters TRACED or the transition is cancelled for
      some reason.  The former is achieved by explicitly calling
      task_clear_jobctl_pending() in ptrace_stop() and the latter by calling
      it at the end of do_signal_stop().
      
      Calling task_clear_jobctl_trapping() at the end of do_signal_stop()
      limits the scope TRAPPING can be used and is fragile in that seemingly
      unrelated changes to tracee's control flow can lead to stuck TRAPPING.
      
      We already have task_clear_jobctl_pending() calls on those cancelling
      events to clear JOBCTL_STOP_PENDING.  Cancellations can be handled by
      making those call sites use JOBCTL_PENDING_MASK instead and updating
      task_clear_jobctl_pending() such that task_clear_jobctl_trapping() is
      called automatically if no stop/trap is pending.
      
      This patch makes the above changes and removes the fallback
      task_clear_jobctl_trapping() call from do_signal_stop().
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      6dfca329
    • T
      job control: introduce JOBCTL_PENDING_MASK and task_clear_jobctl_pending() · 3759a0d9
      Tejun Heo 提交于
      This patch introduces JOBCTL_PENDING_MASK and replaces
      task_clear_jobctl_stop_pending() with task_clear_jobctl_pending()
      which takes an extra @mask argument.
      
      JOBCTL_PENDING_MASK is currently equal to JOBCTL_STOP_PENDING but
      future patches will add more bits.  recalc_sigpending_tsk() is updated
      to use JOBCTL_PENDING_MASK instead.
      
      task_clear_jobctl_pending() takes @mask which in subset of
      JOBCTL_PENDING_MASK and clears the relevant jobctl bits.  If
      JOBCTL_STOP_PENDING is set, other STOP bits are cleared together.  All
      task_clear_jobctl_stop_pending() users are updated to call
      task_clear_jobctl_pending() with JOBCTL_STOP_PENDING which is
      functionally identical to task_clear_jobctl_stop_pending().
      
      This patch doesn't cause any functional change.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      3759a0d9
    • T
      ptrace: relocate set_current_state(TASK_TRACED) in ptrace_stop() · 81be24b8
      Tejun Heo 提交于
      In ptrace_stop(), after arch hook is done, the task state and jobctl
      bits are updated while holding siglock.  The ordering requirement
      there is that TASK_TRACED is set before JOBCTL_TRAPPING is cleared to
      prevent ptracer waiting on TRAPPING doesn't end up waking up TRACED is
      actually set and sees TASK_RUNNING in wait(2).
      
      Move set_current_state(TASK_TRACED) to the top of the block and
      reorganize comments.  This makes the ordering more obvious
      (TASK_TRACED before other updates) and helps future updates to group
      stop participation.
      
      This patch doesn't cause any functional change.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      81be24b8
    • T
      job control: rename signal->group_stop and flags to jobctl and update them · a8f072c1
      Tejun Heo 提交于
      signal->group_stop currently hosts mostly group stop related flags;
      however, it's gonna be used for wider purposes and the GROUP_STOP_
      flag prefix becomes confusing.  Rename signal->group_stop to
      signal->jobctl and rename all GROUP_STOP_* flags to JOBCTL_*.
      
      Bit position macros JOBCTL_*_BIT are defined and JOBCTL_* flags are
      defined in terms of them to allow using bitops later.
      
      While at it, reassign JOBCTL_TRAPPING to bit 22 to better accomodate
      future additions.
      
      This doesn't cause any functional change.
      
      -v2: JOBCTL_*_BIT macros added as suggested by Linus.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Cc: Linus Torvalds <torvalds@linux-foundation.org>
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      a8f072c1
  4. 26 5月, 2011 1 次提交
  5. 09 5月, 2011 2 次提交
    • T
      ptrace: fix signal->wait_chldexit usage in task_clear_group_stop_trapping() · 40ae717d
      Tejun Heo 提交于
      GROUP_STOP_TRAPPING waiting mechanism piggybacks on
      signal->wait_chldexit which is primarily used to implement waiting for
      wait(2) and friends.  When do_wait() waits on signal->wait_chldexit,
      it uses a custom wake up callback, child_wait_callback(), which
      expects the child task which is waking up the parent to be passed in
      as @key to filter out spurious wakeups.
      
      task_clear_group_stop_trapping() used __wake_up_sync() which uses NULL
      @key causing the following oops if the parent was doing do_wait().
      
        BUG: unable to handle kernel NULL pointer dereference at 00000000000002d8
        IP: [<ffffffff810499f9>] child_wait_callback+0x29/0x80
        PGD 1d899067 PUD 1e418067 PMD 0
        Oops: 0000 [#1] PREEMPT SMP
        last sysfs file: /sys/devices/pci0000:00/0000:00:03.0/local_cpus
        CPU 2
        Modules linked in:
      
        Pid: 4498, comm: test-continued Not tainted 2.6.39-rc6-work+ #32 Bochs Bochs
        RIP: 0010:[<ffffffff810499f9>]  [<ffffffff810499f9>] child_wait_callback+0x29/0x80
        RSP: 0000:ffff88001b889bf8  EFLAGS: 00010046
        RAX: 0000000000000000 RBX: ffff88001fab3af8 RCX: 0000000000000000
        RDX: 0000000000000001 RSI: 0000000000000002 RDI: ffff88001d91df20
        RBP: ffff88001b889c08 R08: 0000000000000000 R09: 0000000000000000
        R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000
        R13: ffff88001fb70550 R14: 0000000000000000 R15: 0000000000000001
        FS:  00007f26ccae4700(0000) GS:ffff88001fd00000(0000) knlGS:0000000000000000
        CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
        CR2: 00000000000002d8 CR3: 000000001b8ac000 CR4: 00000000000006e0
        DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
        DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
        Process test-continued (pid: 4498, threadinfo ffff88001b888000, task ffff88001fb88000)
        Stack:
         ffff88001b889c18 ffff88001fb70538 ffff88001b889c58 ffffffff810312f9
         0000000000000001 0000000200000001 ffff88001b889c58 ffff88001fb70518
         0000000000000002 0000000000000082 0000000000000001 0000000000000000
        Call Trace:
         [<ffffffff810312f9>] __wake_up_common+0x59/0x90
         [<ffffffff81035263>] __wake_up_sync_key+0x53/0x80
         [<ffffffff810352a0>] __wake_up_sync+0x10/0x20
         [<ffffffff8105a984>] task_clear_jobctl_trapping+0x44/0x50
         [<ffffffff8105bcbc>] ptrace_stop+0x7c/0x290
         [<ffffffff8105c20a>] do_signal_stop+0x28a/0x2d0
         [<ffffffff8105d27f>] get_signal_to_deliver+0x14f/0x5a0
         [<ffffffff81002175>] do_signal+0x75/0x7b0
         [<ffffffff8100292d>] do_notify_resume+0x5d/0x70
         [<ffffffff8182e36a>] retint_signal+0x46/0x8c
        Code: 00 00 55 48 89 e5 53 48 83 ec 08 0f 1f 44 00 00 8b 47 d8 83 f8 03 74 3a 85 c0 49 89 c8 75 23 89 c0 48 8b 5f e0 4c 8d 0c 40 31 c0 <4b> 39 9c c8 d8 02 00 00 74 1d 48 83 c4 08 5b c9 c3 66 0f 1f 44
      
      Fix it by using __wake_up_sync_key() and passing in the child as @key.
      
      I still think it's a mistake to piggyback on wait_chldexit for this.
      Given the relative low frequency of ptrace use, we would be much
      better off leaving already complex wait_chldexit alone and using bit
      waitqueue.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Reviewed-by: NOleg Nesterov <oleg@redhat.com>
      40ae717d
    • O
      signal: sys_sigprocmask() needs retarget_shared_pending() · 2e4f7c77
      Oleg Nesterov 提交于
      sys_sigprocmask() changes current->blocked by hand. Convert this code
      to use set_current_blocked().
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      2e4f7c77
  6. 28 4月, 2011 11 次提交
  7. 09 4月, 2011 1 次提交
  8. 05 4月, 2011 2 次提交
  9. 04 4月, 2011 3 次提交
    • O
      signal: Turn SIGNAL_STOP_DEQUEUED into GROUP_STOP_DEQUEUED · ee77f075
      Oleg Nesterov 提交于
      This patch moves SIGNAL_STOP_DEQUEUED from signal_struct->flags to
      task_struct->group_stop, and thus makes it per-thread.
      
      Like SIGNAL_STOP_DEQUEUED, GROUP_STOP_DEQUEUED can be false-positive
      after return from get_signal_to_deliver(), this is fine. The only
      purpose of this bit is: we can drop ->siglock after __dequeue_signal()
      returns the sig_kernel_stop() signal and before we call
      do_signal_stop(), in this case we must not miss SIGCONT if it comes in
      between.
      
      But, unlike SIGNAL_STOP_DEQUEUED, GROUP_STOP_DEQUEUED can not be
      false-positive in do_signal_stop() if multiple threads dequeue the
      sig_kernel_stop() signal at the same time.
      
      Consider two threads T1 and T2, SIGTTIN has a hanlder.
      
      	- T1 dequeues SIGTSTP and sets SIGNAL_STOP_DEQUEUED, then
      	  it drops ->siglock
      
      	- SIGCONT comes and clears SIGNAL_STOP_DEQUEUED, SIGTSTP
      	  should be cancelled.
      
      	- T2 dequeues SIGTTIN and sets SIGNAL_STOP_DEQUEUED again.
      	  Since we have a handler we should not stop, T2 returns
      	  to usermode to run the handler.
      
      	- T1 continues, calls do_signal_stop() and wrongly starts
      	  the group stop because SIGNAL_STOP_DEQUEUED was restored
      	  in between.
      
      With or without this change:
      
      	- we need to do something with ptrace_signal() which can
      	  return SIGSTOP, but this needs another discussion
      
      	- SIGSTOP can be lost if it races with the mt exec, will
      	  be fixed later.
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      Signed-off-by: NTejun Heo <tj@kernel.org>
      ee77f075
    • O
      signal: do_signal_stop: Remove the unneeded task_clear_group_stop_pending() · 780006ea
      Oleg Nesterov 提交于
      PF_EXITING or TASK_STOPPED has already called task_participate_group_stop()
      and cleared its ->group_stop. No need to do task_clear_group_stop_pending()
      when we start the new group stop.
      
      Add a small comment to explain the !task_is_stopped() check. Note that this
      check is not exactly right and it can lead to unnecessary stop later if the
      thread is TASK_PTRACED. What we need is task_participated_in_group_stop(),
      this will be solved later.
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      Signed-off-by: NTejun Heo <tj@kernel.org>
      780006ea
    • O
      signal: prepare_signal(SIGCONT) shouldn't play with TIF_SIGPENDING · 1deac632
      Oleg Nesterov 提交于
      prepare_signal(SIGCONT) should never set TIF_SIGPENDING or wake up
      the TASK_INTERRUPTIBLE threads. We are going to call complete_signal()
      which should pick the right thread correctly. All we need is to wake
      up the TASK_STOPPED threads.
      
      If the task was stopped, it can't return to usermode without taking
      ->siglock. Otherwise we don't care, and the spurious TIF_SIGPENDING
      can't be useful.
      
      The comment says:
      
      	* If there is a handler for SIGCONT, we must make
      	* sure that no thread returns to user mode before
      	* we post the signal
      
      It is not clear what this means. Probably, "when there's only a single
      thread" and this continues to be true. Otherwise, even if this SIGCONT
      is not private, with or without this change only one thread can dequeue
      SIGCONT, other threads can happily return to user mode before before
      that thread handles this signal.
      
      Note also that wake_up_state(t, __TASK_STOPPED) can't race with the task
      which changes its state, TASK_STOPPED state is protected by ->siglock as
      well.
      
      In short: when it comes to signal delivery, SIGCONT is the normal signal
      and does not need any special support.
      Signed-off-by: NOleg Nesterov <oleg@redhat.com>
      Signed-off-by: NTejun Heo <tj@kernel.org>
      1deac632
  10. 31 3月, 2011 1 次提交
  11. 29 3月, 2011 1 次提交
    • R
      Relax si_code check in rt_sigqueueinfo and rt_tgsigqueueinfo · 243b422a
      Roland Dreier 提交于
      Commit da48524e ("Prevent rt_sigqueueinfo and rt_tgsigqueueinfo
      from spoofing the signal code") made the check on si_code too strict.
      There are several legitimate places where glibc wants to queue a
      negative si_code different from SI_QUEUE:
      
       - This was first noticed with glibc's aio implementation, which wants
         to queue a signal with si_code SI_ASYNCIO; the current kernel
         causes glibc's tst-aio4 test to fail because rt_sigqueueinfo()
         fails with EPERM.
      
       - Further examination of the glibc source shows that getaddrinfo_a()
         wants to use SI_ASYNCNL (which the kernel does not even define).
         The timer_create() fallback code wants to queue signals with SI_TIMER.
      
      As suggested by Oleg Nesterov <oleg@redhat.com>, loosen the check to
      forbid only the problematic SI_TKILL case.
      Reported-by: NKlaus Dittrich <kladit@arcor.de>
      Acked-by: NJulien Tinnes <jln@google.com>
      Cc: <stable@kernel.org>
      Signed-off-by: NRoland Dreier <roland@purestorage.com>
      Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
      243b422a
  12. 24 3月, 2011 1 次提交
  13. 23 3月, 2011 5 次提交
    • 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
    • T
      job control: Notify the real parent of job control events regardless of ptrace · ceb6bd67
      Tejun Heo 提交于
      With recent changes, job control and ptrace stopped states are
      properly separated and accessible to the real parent and the ptracer
      respectively; however, notifications of job control stopped/continued
      events to the real parent while ptraced are still missing.
      
      A ptracee participates in group stop in ptrace_stop() but the
      completion isn't notified.  If participation results in completion of
      group stop, notify the real parent of the event.  The ptrace and group
      stops are separate and can be handled as such.
      
      However, when the real parent and the ptracer are in the same thread
      group, only the ptrace stop event is visible through wait(2) and the
      duplicate notifications are different from the current behavior and
      are confusing.  Suppress group stop notification in such cases.
      
      The continued state is shared between the real parent and the ptracer
      but is only meaningful to the real parent.  Always notify the real
      parent and notify the ptracer too for backward compatibility.  Similar
      to stop notification, if the real parent is the ptracer, suppress a
      duplicate notification.
      
      Test case follows.
      
        #include <stdio.h>
        #include <unistd.h>
        #include <time.h>
        #include <errno.h>
        #include <sys/types.h>
        #include <sys/ptrace.h>
        #include <sys/wait.h>
      
        int main(void)
        {
      	  const struct timespec ts100ms = { .tv_nsec = 100000000 };
      	  pid_t tracee, tracer;
      	  siginfo_t si;
      	  int i;
      
      	  tracee = fork();
      	  if (tracee == 0) {
      		  while (1) {
      			  printf("tracee: SIGSTOP\n");
      			  raise(SIGSTOP);
      			  nanosleep(&ts100ms, NULL);
      			  printf("tracee: SIGCONT\n");
      			  raise(SIGCONT);
      			  nanosleep(&ts100ms, NULL);
      		  }
      	  }
      
      	  waitid(P_PID, tracee, &si, WSTOPPED | WNOHANG | WNOWAIT);
      
      	  tracer = fork();
      	  if (tracer == 0) {
      		  nanosleep(&ts100ms, NULL);
      		  ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
      
      		  for (i = 0; i < 11; i++) {
      			  si.si_pid = 0;
      			  waitid(P_PID, tracee, &si, WSTOPPED);
      			  if (si.si_pid && si.si_code == CLD_TRAPPED)
      				  ptrace(PTRACE_CONT, tracee, NULL,
      					 (void *)(long)si.si_status);
      		  }
      		  printf("tracer: EXITING\n");
      		  return 0;
      	  }
      
      	  while (1) {
      		  si.si_pid = 0;
      		  waitid(P_PID, tracee, &si, WSTOPPED | WCONTINUED | WEXITED);
      		  if (si.si_pid)
      			  printf("mommy : WAIT status=%02d code=%02d\n",
      				 si.si_status, si.si_code);
      	  }
      	  return 0;
        }
      
      Before this patch, while ptraced, the real parent doesn't get
      notifications for job control events, so although it can access those
      events, the later waitid(2) call never wakes up.
      
        tracee: SIGSTOP
        mommy : WAIT status=19 code=05
        tracee: SIGCONT
        tracee: SIGSTOP
        tracee: SIGCONT
        tracee: SIGSTOP
        tracee: SIGCONT
        tracee: SIGSTOP
        tracer: EXITING
        mommy : WAIT status=19 code=05
        ^C
      
      After this patch, it works as expected.
      
        tracee: SIGSTOP
        mommy : WAIT status=19 code=05
        tracee: SIGCONT
        mommy : WAIT status=18 code=06
        tracee: SIGSTOP
        mommy : WAIT status=19 code=05
        tracee: SIGCONT
        mommy : WAIT status=18 code=06
        tracee: SIGSTOP
        mommy : WAIT status=19 code=05
        tracee: SIGCONT
        mommy : WAIT status=18 code=06
        tracee: SIGSTOP
        tracer: EXITING
        mommy : WAIT status=19 code=05
        ^C
      
      -v2: Oleg pointed out that
      
           * Group stop notification to the real parent should also happen
             when ptracer detach races with ptrace_stop().
      
           * real_parent_is_ptracer() should be testing thread group
             equality not the task itself as wait(2) and stop/cont
             notifications are normally thread-group wide.
      
           Both issues are fixed accordingly.
      
      -v3: real_parent_is_ptracer() updated to test child->real_parent
           instead of child->group_leader->real_parent per Oleg's
           suggestion.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Acked-by: NOleg Nesterov <oleg@redhat.com>
      ceb6bd67
    • T
      job control: Job control stop notifications should always go to the real parent · 62bcf9d9
      Tejun Heo 提交于
      The stopped notifications in do_signal_stop() and exit_signals() are
      always for the completion of job control.  The one in do_signal_stop()
      may be delivered to the ptracer if PTRACE_ATTACH races with
      notification and the one in exit_signals() if task exits while
      ptraced.
      
      In both cases, the notifications are meaningless and confusing to the
      ptracer as it never accesses the group stop state while the real
      parent would miss notifications for the events it is watching.
      
      Make sure these notifications always go to the real parent by calling
      do_notify_parent_cld_stop() with %false @for_ptrace.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Acked-by: NOleg Nesterov <oleg@redhat.com>
      62bcf9d9
    • T
      job control: Add @for_ptrace to do_notify_parent_cldstop() · 75b95953
      Tejun Heo 提交于
      Currently, do_notify_parent_cldstop() determines whether the
      notification is for the real parent or ptracer.  Move the decision to
      the caller by adding @for_ptrace parameter to
      do_notify_parent_cldstop().  All the callers are updated to pass
      task_ptrace(target_task), so this patch doesn't cause any behavior
      difference.
      
      While at it, add function comment to do_notify_parent_cldstop().
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Acked-by: NOleg Nesterov <oleg@redhat.com>
      75b95953
    • T
      job control: Don't set group_stop exit_code if re-entering job control stop · 408a37de
      Tejun Heo 提交于
      While ptraced, a task may be resumed while the containing process is
      still job control stopped.  If the task receives another stop signal
      in this state, it will still initiate group stop, which generates
      group_exit_code, which the real parent would be able to see once the
      ptracer detaches.
      
      In this scenario, the real parent may see two consecutive CLD_STOPPED
      events from two stop signals without intervening SIGCONT, which
      normally is impossible.
      
      Test case follows.
      
        #include <stdio.h>
        #include <unistd.h>
        #include <sys/ptrace.h>
        #include <sys/wait.h>
      
        int main(void)
        {
      	  pid_t tracee;
      	  siginfo_t si;
      
      	  tracee = fork();
      	  if (!tracee)
      		  while (1)
      			  pause();
      
      	  kill(tracee, SIGSTOP);
      	  waitid(P_PID, tracee, &si, WSTOPPED);
      
      	  if (!fork()) {
      		  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);
      		  ptrace(PTRACE_DETACH, tracee, NULL, NULL);
      		  return 0;
      	  }
      
      	  while (1) {
      		  si.si_pid = 0;
      		  waitid(P_PID, tracee, &si, WSTOPPED | WNOHANG);
      		  if (si.si_pid)
      			  printf("st=%02d c=%02d\n", si.si_status, si.si_code);
      	  }
      	  return 0;
        }
      
      Before the patch, the latter waitid() in polling mode reports the
      second stopped event generated by the implied SIGSTOP of
      PTRACE_ATTACH.
      
        st=19 c=05
        ^C
      
      After the patch, the second event is not reported.
      Signed-off-by: NTejun Heo <tj@kernel.org>
      Acked-by: NOleg Nesterov <oleg@redhat.com>
      408a37de