From: Tejun Heo <tj@kernel.org>
To: oleg@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, bdonlan@gmail.com,
Tejun Heo <tj@kernel.org>
Subject: [PATCH 10/10] ptrace: implement group stop notification for ptracer
Date: Mon, 16 May 2011 20:17:29 +0200 [thread overview]
Message-ID: <1305569849-10448-11-git-send-email-tj@kernel.org> (raw)
In-Reply-To: <1305569849-10448-1-git-send-email-tj@kernel.org>
Currently there's no way for ptracer to find out whether group stop
that tracee was in finished other than polling with PTRACE_GETSIGINFO.
Also, tracer can't detect new group stop started by an untraced thread
if tracee is already trapped. 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 triggers STOP trap but is sticky until the next
PTRACE_GETSIGINFO. As GETSIGINFO exports the current group stop
state, this guarantees that tracer checks the current group stop state
at least once after group stop state change. Stickiness is necessary
because notification trap may race with PTRACE_CONT for other traps
and get lost.
Note that simply scheduling such trap isn't enough. If tracee is
running (PTRACE_CONT'd from group stop trap), the usual trapping -
setting NOTIFY followed by the usual signal_wake_up() - is enough;
however, if tracee is trapped, the scheduled trap won't happen until
the trap is continued.
This is solved by re-trapping if tracee is in STOP trap. Along with
JOBCTL_TRAP_NOTIFY, JOBCTL_TRAPPING is set and tracee is woken up from
TASK_TRACED. Tracee then (re-)enters INTERRUPT trap generating
notification for tracer. TRAPPING hides the TRACED -> RUNNING ->
TRACED transition from tracer.
Many ptrace requests expect tracee to remain trapped until they
finish. Such conditions are marked with JOBCTL_BLOCK_NOTIFY and if
notification happens while BLOCK_NOTIFY is set, JOBCTL_TRAPPING is set
but the actual wake up and re-trapping takes place when the ptrace
request finishes. This is safe as the only task which can wait for
TRAPPING is the ptracer.
Re-trapping is used only for STOP trap. If tracer wants to get
notified about group stop, it either leaves tracee in the initial STOP
trap or puts it into STOP trap using PTRACE_INTERRUPT. If STOP trap
is scheduled while tracee is already in a trap, it's guaranteed that
tracee will enter a trap without returning to userland, so tracer
doesn't lose any control over tracee execution for group stop
notification.
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) {
int last_stopped = 0, stopped;
siginfo_t si;
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
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;
}
stopped = !!si.si_status;
if (stopped != last_stopped)
printf("tracer: stopped=%d signo=%d\n",
stopped, si.si_signo);
last_stopped = stopped;
if (!stopped)
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 gets notification of group stop state
changes and can track stopped state without polling PTRACE_GETSIGINFO.
# ./test-gstop-notify
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: Tejun Heo <tj@kernel.org>
---
include/linux/sched.h | 3 +-
kernel/ptrace.c | 11 +++++++-
kernel/signal.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 1f082d9..5573930 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1785,10 +1785,11 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *
#define JOBCTL_STOP_PENDING (1 << 17) /* task should stop for group stop */
#define JOBCTL_STOP_CONSUME (1 << 18) /* consume group stop count */
#define JOBCTL_TRAP_STOP (1 << 19) /* trap for STOP */
+#define JOBCTL_TRAP_NOTIFY (1 << 20) /* sticky trap for notifications */
#define JOBCTL_TRAPPING (1 << 21) /* switching to TRACED */
#define JOBCTL_BLOCK_NOTIFY (1 << 22) /* block NOTIFY re-traps */
-#define JOBCTL_TRAP_MASK JOBCTL_TRAP_STOP
+#define JOBCTL_TRAP_MASK (JOBCTL_TRAP_STOP | JOBCTL_TRAP_NOTIFY)
#define JOBCTL_PENDING_MASK (JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK)
extern void task_clear_jobctl_pending(struct task_struct *task,
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d382f81..2640761 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -130,6 +130,7 @@ void __ptrace_unlink(struct task_struct *child)
* Clear jobctl bits owned by this tracer along with STOP_PENDING,
* which is reinstated below if necessary.
*/
+ child->jobctl &= ~JOBCTL_BLOCK_NOTIFY;
task_clear_jobctl_pending(child, JOBCTL_PENDING_MASK);
/*
@@ -617,6 +618,9 @@ static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
info->si_signo = child->jobctl & JOBCTL_STOP_SIGMASK;
WARN_ON_ONCE(!info->si_signo);
}
+
+ /* tracer got siginfo, clear the sticky trap */
+ child->jobctl &= ~JOBCTL_TRAP_NOTIFY;
}
out_unlock:
unlock_task_sighand(child, &flags);
@@ -923,9 +927,14 @@ static void ptrace_put_task_struct(struct task_struct *child)
/*
* Make sure @chlid is still ptraced by us and clear BLOCK_NOTIFY.
+ * If TRAPPING is set, it means NOTIFY occurred in-between and
+ * re-trap was blocked. Trigger re-trap.
*/
- if (likely((child->ptrace & PT_PTRACED) && child->parent == current))
+ if (likely((child->ptrace & PT_PTRACED) && child->parent == current)) {
child->jobctl &= ~JOBCTL_BLOCK_NOTIFY;
+ if (child->jobctl & JOBCTL_TRAPPING)
+ signal_wake_up(child, task_is_traced(child));
+ }
unlock_task_sighand(child, &flags);
out_put:
diff --git a/kernel/signal.c b/kernel/signal.c
index c05f4d1..b794363 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -784,6 +784,60 @@ static int check_kill_permission(int sig, struct siginfo *info,
return security_task_kill(t, info, sig, 0);
}
+/**
+ * ptrace_trap_notify - schedule trap to notify ptracer
+ * @t: tracee wanting to notify tracer
+ *
+ * This function schedules sticky ptrace trap which is cleared on
+ * PTRACE_GETSIGINFO to notify ptracer of an event. @t must have been
+ * seized by ptracer.
+ *
+ * If @t is running, STOP trap will be taken. If already trapped for STOP,
+ * it will re-trap. If trapped for other traps, STOP trap will be
+ * eventually taken without returning to userland after the existing traps
+ * are finished by PTRACE_CONT.
+ *
+ * CONTEXT:
+ * Must be called with @task->sighand->siglock held.
+ */
+static void ptrace_trap_notify(struct task_struct *t)
+{
+ siginfo_t *si = t->last_siginfo;
+
+ WARN_ON_ONCE(!(t->ptrace & PT_SEIZED));
+ assert_spin_locked(&t->sighand->siglock);
+
+ /*
+ * @t is being ptraced and new SEIZE behavior is in effect.
+ * Schedule sticky trap which will clear on the next GETSIGINFO.
+ */
+ t->jobctl |= JOBCTL_TRAP_NOTIFY;
+
+ /*
+ * If @t is currently trapped for STOP, it should re-trap with new
+ * exit_code indicating continuation so that the ptracer can notice
+ * the event; otherwise, use normal signal delivery wake up.
+ *
+ * The re-trapping sets JOBCTL_TRAPPING such that the transition is
+ * hidden from the ptracer.
+ *
+ * This means that if @t is trapped for other reasons than STOP,
+ * the notification trap won't be delievered until the current one
+ * is complete. This is the intended behavior.
+ *
+ * Note that if JOBCTL_BLOCK_NOTIFY, TRAPPING is set but actual
+ * re-trap doesn't happen. This is used to avoid waking up while
+ * ptrace request is in progress. The ptracer will notice TRAPPING
+ * is set on request completion and trigger re-trap.
+ */
+ if (task_is_traced(t) && si && si->si_code == PTRACE_STOP_SI_CODE) {
+ t->jobctl |= JOBCTL_TRAPPING;
+ if (!(t->jobctl & JOBCTL_BLOCK_NOTIFY))
+ signal_wake_up(t, true);
+ } else
+ signal_wake_up(t, false);
+}
+
/*
* Handle magic process-wide effects of stop/continue signals. Unlike
* the signal actions, these happen immediately at signal-generation
@@ -822,7 +876,10 @@ static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns)
do {
task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING);
rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
- wake_up_state(t, __TASK_STOPPED);
+ if (likely(!(t->ptrace & PT_SEIZED)))
+ wake_up_state(t, __TASK_STOPPED);
+ else
+ ptrace_trap_notify(t);
} while_each_thread(p, t);
/*
@@ -1938,7 +1995,10 @@ static bool do_signal_stop(int signr)
if (!(t->flags & PF_EXITING) && !task_is_stopped(t)) {
t->jobctl |= signr | gstop;
sig->group_stop_count++;
- signal_wake_up(t, 0);
+ if (likely(!(t->ptrace & PT_SEIZED)))
+ signal_wake_up(t, 0);
+ else
+ ptrace_trap_notify(t);
}
}
}
@@ -1976,10 +2036,10 @@ static bool do_signal_stop(int signr)
schedule();
} else {
/*
- * While ptraced, group stop is handled by STOP trap.
+ * While ptraced, group stop is handled by NOTIFY trap.
* Schedule it and let the caller deal with it.
*/
- current->jobctl |= JOBCTL_TRAP_STOP;
+ current->jobctl |= JOBCTL_TRAP_NOTIFY;
spin_unlock_irq(¤t->sighand->siglock);
}
--
1.7.1
next prev parent reply other threads:[~2011-05-16 18:17 UTC|newest]
Thread overview: 88+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-05-16 18:17 [PATCHSET ptrace] ptrace: implement PTRACE_SEIZE/INTERRUPT and group stop notification, take#2 Tejun Heo
2011-05-16 18:17 ` [PATCH 01/10] signal: remove three noop tracehooks Tejun Heo
2011-05-17 16:22 ` Christoph Hellwig
2011-05-17 16:27 ` Tejun Heo
2011-05-18 18:45 ` Oleg Nesterov
2011-05-19 12:11 ` Tejun Heo
2011-05-19 16:10 ` Oleg Nesterov
2011-05-16 18:17 ` [PATCH 02/10] job control: introduce JOBCTL_TRAP_STOP and use it for group stop trap Tejun Heo
2011-05-18 16:48 ` Oleg Nesterov
2011-05-18 16:57 ` Oleg Nesterov
2011-05-19 10:19 ` Tejun Heo
2011-05-19 16:19 ` Oleg Nesterov
2011-05-16 18:17 ` [PATCH 03/10] ptrace: implement PTRACE_SEIZE Tejun Heo
2011-05-18 0:40 ` Denys Vlasenko
2011-05-18 9:55 ` Tejun Heo
2011-05-18 10:44 ` Denys Vlasenko
2011-05-18 11:14 ` Tejun Heo
2011-05-19 14:17 ` Tejun Heo
2011-05-19 15:02 ` Tejun Heo
2011-05-19 19:31 ` Pedro Alves
2011-05-19 22:42 ` Denys Vlasenko
2011-05-19 23:00 ` Pedro Alves
2011-05-20 1:44 ` Denys Vlasenko
2011-05-20 8:56 ` Pedro Alves
2011-05-20 9:12 ` Tejun Heo
2011-05-20 9:07 ` Tejun Heo
2011-05-20 9:27 ` Pedro Alves
2011-05-20 9:31 ` Tejun Heo
2011-05-24 9:49 ` Pedro Alves
2011-05-24 12:00 ` Tejun Heo
2011-05-24 12:36 ` Pedro Alves
2011-05-24 14:02 ` Tejun Heo
2011-05-24 14:55 ` Pedro Alves
2011-05-25 18:18 ` Oleg Nesterov
2011-05-26 9:10 ` Tejun Heo
2011-05-26 10:01 ` Pedro Alves
2011-05-26 10:11 ` Tejun Heo
2011-05-26 14:55 ` Oleg Nesterov
2011-05-23 13:09 ` Oleg Nesterov
2011-05-23 12:43 ` Oleg Nesterov
2011-05-24 10:28 ` Tejun Heo
2011-05-25 18:29 ` Oleg Nesterov
2011-05-26 9:14 ` Tejun Heo
2011-05-26 15:01 ` Oleg Nesterov
2011-05-27 18:21 ` Tejun Heo
2011-05-30 19:22 ` Oleg Nesterov
[not found] ` <BANLkTimupSd774N-VBoswOj+Dza=5ofvWQ@mail.gmail.com>
2011-05-31 19:08 ` Oleg Nesterov
2011-05-31 21:32 ` Linus Torvalds
2011-06-01 20:04 ` Oleg Nesterov
2011-06-01 5:34 ` Tejun Heo
2011-06-01 20:08 ` Oleg Nesterov
2011-06-02 5:01 ` Tejun Heo
2011-05-18 18:17 ` Oleg Nesterov
2011-05-19 10:34 ` Tejun Heo
2011-05-16 18:17 ` [PATCH 04/10] ptrace: implement PTRACE_INTERRUPT Tejun Heo
2011-05-18 18:38 ` Oleg Nesterov
2011-05-19 12:07 ` Tejun Heo
2011-05-19 16:21 ` Oleg Nesterov
2011-05-16 18:17 ` [PATCH 05/10] ptrace: restructure ptrace_getsiginfo() Tejun Heo
2011-05-16 18:17 ` [PATCH 06/10] ptrace: add siginfo.si_pt_flags Tejun Heo
2011-05-16 18:17 ` [PATCH 07/10] ptrace: make group stop state visible via PTRACE_GETSIGINFO Tejun Heo
2011-05-19 16:27 ` Oleg Nesterov
2011-05-19 16:40 ` Tejun Heo
2011-05-16 18:17 ` [PATCH 08/10] ptrace: don't let PTRACE_SETSIGINFO override __SI_TRAP siginfo Tejun Heo
2011-05-16 18:17 ` [PATCH 09/10] ptrace: add JOBCTL_BLOCK_NOTIFY Tejun Heo
2011-05-19 16:32 ` Oleg Nesterov
2011-05-19 16:44 ` Tejun Heo
2011-05-19 16:48 ` Oleg Nesterov
2011-05-19 16:58 ` Tejun Heo
2011-05-16 18:17 ` Tejun Heo [this message]
2011-05-19 16:32 ` [PATCH 10/10] ptrace: implement group stop notification for ptracer Oleg Nesterov
2011-05-19 16:57 ` Tejun Heo
2011-05-19 17:13 ` Oleg Nesterov
2011-05-19 22:48 ` Denys Vlasenko
2011-05-20 8:59 ` Tejun Heo
2011-05-23 13:34 ` Oleg Nesterov
2011-05-20 8:46 ` Tejun Heo
2011-05-19 16:58 ` Oleg Nesterov
2011-05-23 11:45 ` Oleg Nesterov
2011-05-24 13:44 ` Tejun Heo
2011-05-24 15:44 ` Tejun Heo
2011-05-26 14:44 ` Oleg Nesterov
2011-05-28 7:32 ` Tejun Heo
2011-05-18 18:50 ` [PATCHSET ptrace] ptrace: implement PTRACE_SEIZE/INTERRUPT and group stop notification, take#2 Oleg Nesterov
2011-05-19 12:08 ` Tejun Heo
2011-05-19 15:04 ` Linus Torvalds
2011-05-19 15:19 ` Tejun Heo
2011-05-19 22:45 ` Denys Vlasenko
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=1305569849-10448-11-git-send-email-tj@kernel.org \
--to=tj@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=bdonlan@gmail.com \
--cc=indan@nul.nu \
--cc=jan.kratochvil@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=oleg@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).