linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tejun Heo <tj@kernel.org>
To: oleg@redhat.com, roland@redhat.com, jan.kratochvil@redhat.com,
	vda.linux@googlemail.com
Cc: linux-kernel@vger.kernel.org, torvalds@linux-foundation.org,
	akpm@linux-foundation.org, indan@nul.nu
Subject: [PATCH UPDATED 4/8] job control: Allow access to job control events through ptracees
Date: Tue, 22 Mar 2011 12:10:39 +0100	[thread overview]
Message-ID: <20110322111039.GQ12003@htj.dyndns.org> (raw)
In-Reply-To: <1299614199-25142-5-git-send-email-tj@kernel.org>

Currently a real parent can't access job control stopped/continued
events through a ptraced child.  This utterly breaks job control when
the children are ptraced.

For example, if a program is run from an interactive shell and then
strace(1) attaches to it, pressing ^Z would send SIGTSTP and strace(1)
would notice it but the shell has no way to tell whether the child
entered job control stop and thus can't tell when to take over the
terminal - leading to awkward lone ^Z on the terminal.

Because the job control and ptrace stopped states are independent,
there is no reason to prevent real parents from accessing the stopped
state regardless of ptrace.  The continued state isn't separate but
ptracers don't have any use for them as ptracees can never resume
without explicit command from their ptracers, so as long as ptracers
don't consume it, it should be fine.

Although this is a behavior change, because the previous behavior is
utterly broken when viewed from real parents and the change is only
visible to real parents, I don't think it's necessary to make this
behavior optional.

One situation to be careful about is when a task from the real
parent's group is ptracing.  The parent group is the recipient of both
ptrace and job control stop events and one stop can be reported as
both job control and ptrace stops.  As this can break the current
ptrace users, suppress job control stopped events for these cases.

If a real parent ptracer wants to know about both job control and
ptrace stops, it can create a separate process to serve the role of
real parent.

Note that this only updates wait(2) side of things.  The real parent
can access the states via wait(2) but still is not properly notified
(woken up and delivered signal).  Test case polls wait(2) with WNOHANG
to work around.  Notification will be updated by future patches.

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 | WNOHANG);
		  if (si.si_pid)
			  printf("mommy : WAIT status=%02d code=%02d\n",
				 si.si_status, si.si_code);
		  nanosleep(&ts100ms, NULL);
	  }
	  return 0;
  }

Before the patch, while ptraced, the parent can't see any job control
events.

  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 the patch,

  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 wait(2) should be suppressed for the real
     parent's group instead of only the real parent task itself.
     Updated accordingly.

Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
---
 kernel/exit.c |   41 +++++++++++++++++++++++++++++++++--------
 1 file changed, 33 insertions(+), 8 deletions(-)

Index: work/kernel/exit.c
===================================================================
--- work.orig/kernel/exit.c
+++ work/kernel/exit.c
@@ -1541,17 +1541,19 @@ static int wait_consider_task(struct wai
 	if (p->exit_state == EXIT_DEAD)
 		return 0;
 
-	if (likely(!ptrace) && unlikely(task_ptrace(p))) {
+	/* slay zombie? */
+	if (p->exit_state == EXIT_ZOMBIE) {
 		/*
-		 * This child is hidden by ptrace.
-		 * We aren't allowed to see it now, but eventually we will.
+		 * A zombie ptracee is only visible to its ptracer.
+		 * Notification and reaping will be cascaded to the real
+		 * parent when the ptracer detaches.
 		 */
-		wo->notask_error = 0;
-		return 0;
-	}
+		if (likely(!ptrace) && unlikely(task_ptrace(p))) {
+			/* it will become visible, clear notask_error */
+			wo->notask_error = 0;
+			return 0;
+		}
 
-	/* slay zombie? */
-	if (p->exit_state == EXIT_ZOMBIE) {
 		/* we don't reap group leaders with subthreads */
 		if (!delay_group_leader(p))
 			return wait_task_zombie(wo, p);
@@ -1580,15 +1582,38 @@ static int wait_consider_task(struct wai
 			wo->notask_error = 0;
 	} else {
 		/*
+		 * If @p is ptraced by a task in its real parent's group,
+		 * hide group stop/continued state when looking at @p as
+		 * the real parent; otherwise, a single stop can be
+		 * reported twice as group and ptrace stops.
+		 *
+		 * If a ptracer wants to distinguish the two events for its
+		 * own children, it should create a separate process which
+		 * takes the role of real parent.
+		 */
+		if (likely(!ptrace) && task_ptrace(p) &&
+		    same_thread_group(p->parent, p->real_parent))
+			return 0;
+
+		/*
 		 * @p is alive and it's gonna stop, continue or exit, so
 		 * there always is something to wait for.
 		 */
 		wo->notask_error = 0;
 	}
 
+	/*
+	 * Wait for stopped.  Depending on @ptrace, different stopped state
+	 * is used and the two don't interact with each other.
+	 */
 	if (task_stopped_code(p, ptrace))
 		return wait_task_stopped(wo, ptrace, p);
 
+	/*
+	 * Wait for continued.  There's only one continued state and the
+	 * ptracer can consume it which can confuse the real parent.  Don't
+	 * use WCONTINUED from ptracer.  You don't need or want it.
+	 */
 	return wait_task_continued(wo, p);
 }
 

  parent reply	other threads:[~2011-03-22 11:10 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-03-08 19:56 [RFC PATCHSET] ptrace,signal: Fix notifications to the real parent while ptraced Tejun Heo
2011-03-08 19:56 ` [PATCH 1/8] job control: Don't set group_stop exit_code if re-entering job control stop Tejun Heo
2011-03-21 13:20   ` Oleg Nesterov
2011-03-21 15:52     ` Tejun Heo
2011-03-22 18:44       ` Oleg Nesterov
2011-03-23  8:44         ` Tejun Heo
2011-03-23 16:40           ` Oleg Nesterov
2011-03-23 17:02             ` Tejun Heo
2011-03-23 17:09               ` Oleg Nesterov
2011-03-23 17:22                 ` Tejun Heo
2011-03-08 19:56 ` [PATCH 2/8] job control: Small reorganization of wait_consider_task() Tejun Heo
2011-03-08 19:56 ` [PATCH 3/8] job control: Fix ptracer wait(2) hang and explain notask_error clearing Tejun Heo
2011-03-21 15:19   ` Oleg Nesterov
2011-03-21 16:09     ` Oleg Nesterov
2011-03-21 16:12     ` Tejun Heo
2011-03-22 19:08       ` Oleg Nesterov
2011-03-22 10:51   ` [PATCH UPDATED " Tejun Heo
2011-03-08 19:56 ` [PATCH 4/8] job control: Allow access to job control events through ptracees Tejun Heo
2011-03-21 16:39   ` Oleg Nesterov
2011-03-21 17:20     ` Tejun Heo
2011-03-22 11:10   ` Tejun Heo [this message]
2011-03-08 19:56 ` [PATCH 5/8] job control: Add @for_ptrace to do_notify_parent_cldstop() Tejun Heo
2011-03-08 19:56 ` [PATCH 6/8] job control: Job control stop notifications should always go to the real parent Tejun Heo
2011-03-21 17:12   ` Oleg Nesterov
2011-03-08 19:56 ` [PATCH 7/8] job control: Notify the real parent of job control events regardless of ptrace Tejun Heo
2011-03-21 17:43   ` Oleg Nesterov
2011-03-22  8:04     ` Tejun Heo
2011-03-22 19:44       ` Oleg Nesterov
2011-03-23  9:17         ` Tejun Heo
2011-03-23  9:24           ` Tejun Heo
2011-03-23 16:46             ` Oleg Nesterov
2011-03-23 16:59               ` Tejun Heo
2011-03-23 17:07                 ` Oleg Nesterov
2011-03-23 17:20                   ` Tejun Heo
2011-03-23 17:17                     ` Oleg Nesterov
2011-03-22 11:30   ` [PATCH UPDATED " Tejun Heo
2011-03-08 19:56 ` [PATCH 8/8] job control: Don't send duplicate job control stop notification while ptraced Tejun Heo
2011-03-21 17:48   ` Oleg Nesterov
2011-03-08 20:01 ` [RFC PATCHSET] ptrace,signal: Fix notifications to the real parent " Linus Torvalds
2011-03-09 16:50 ` Oleg Nesterov
2011-03-22 10:20 ` [PATCH 0.1/8] ptrace: Collapse ptrace_untrace() into __ptrace_unlink() Tejun Heo
2011-03-22 10:20 ` [PATCH 0.2/8] ptrace: Always put ptracee into appropriate execution state Tejun Heo
2011-03-22 20:33   ` Oleg Nesterov
2011-03-23  8:00     ` Tejun Heo
2011-03-22 13:11 ` [RFC PATCHSET] ptrace,signal: Fix notifications to the real parent while ptraced Tejun Heo
2011-03-22 20:59   ` Oleg Nesterov
2011-03-23  8:48     ` Tejun Heo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110322111039.GQ12003@htj.dyndns.org \
    --to=tj@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=indan@nul.nu \
    --cc=jan.kratochvil@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=oleg@redhat.com \
    --cc=roland@redhat.com \
    --cc=torvalds@linux-foundation.org \
    --cc=vda.linux@googlemail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).