[PATCH 31] ptrace_report_clone: rework the stop/resume logic
Oleg Nesterov
oleg at redhat.com
Sun Sep 13 20:11:13 UTC 2009
TODO:
1. CLEANUP !!!!
2. CLONE_UNTRACE check is wrong
3. child->pid is wrong, we must use tracer's namespace
Note:
Ugly, but we have to add context->o_did_clone_vfork bolean,
it is true if the last do_fork() had CLONE_VFORK.
We can't figure out whether we need PTRACE_O_TRACEVFORKDONE
at ptrace_report_clone() time, the tracer can change options
after we report PTRACE_EVENT_VFORK.
Passes this test:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <assert.h>
#define WEVENT(s) ((s & 0xFF0000) >> 16)
static unsigned long __step(int line, int pid, int o, int e)
{
int stat, cont = PTRACE_CONT;
unsigned long msg;
if (o == PTRACE_SYSCALL) {
cont = o;
} else if (o) {
assert(0 == ptrace(PTRACE_SETOPTIONS, pid, 0, o));
}
assert(0 == ptrace(cont, pid, 0, 0));
assert(waitpid(pid, &stat, __WALL) == pid);
assert(WIFSTOPPED(stat));
assert(WSTOPSIG(stat) == SIGTRAP);
assert(WEVENT(stat) == e);
assert(0 == ptrace(PTRACE_GETEVENTMSG, pid, 0, &msg));
return msg;
}
#define step(pid, o, e) __step(__LINE__, pid, o, e)
#define T_FORK_A 0x01
#define T_FORK_Z 0x02
#define T_SYSCALL 0x04
#define T_SIGNAL 0x08
#define T_ALL (T_FORK_A | T_FORK_Z | T_SYSCALL | T_SIGNAL)
static void p_todo(int todo)
{
printf("todo: ");
#define P(n) if (todo & n) printf(#n " ")
P(T_FORK_A); P(T_FORK_Z); P(T_SYSCALL); P(T_SIGNAL);
#undef P
if (todo & ~T_ALL) printf("ERR!!!");
printf("\n");
}
static void tst(int todo)
{
int pid, cpid=-1, stat;
long msg;
p_todo(todo);
pid = fork();
if (!pid) {
int vp;
if (!(todo & T_SIGNAL))
signal(SIGCHLD, SIG_IGN);
assert(0 == ptrace(PTRACE_TRACEME, 0,0,0));
kill(getpid(), SIGSTOP);
vp = vfork();
exit(0x43);
}
assert(wait(&stat) == pid);
assert(WIFSTOPPED(stat) && WSTOPSIG(stat) == SIGSTOP);
if (todo & T_SYSCALL) {
msg = step(pid, PTRACE_SYSCALL, 0);
assert(msg == 0);
}
if (todo & T_FORK_A) {
cpid = step(pid, PTRACE_O_TRACEVFORK, PTRACE_EVENT_VFORK);
assert(waitpid(cpid, &stat, __WALL) == cpid);
assert(WIFSTOPPED(stat));
assert(WSTOPSIG(stat) == SIGSTOP);
assert(0 == ptrace(PTRACE_CONT, cpid, 0, 0));
assert(waitpid(cpid, &stat, __WALL) == cpid);
assert(WIFEXITED(stat));
}
if (todo & T_FORK_Z) {
msg = step(pid, PTRACE_O_TRACEVFORKDONE, PTRACE_EVENT_VFORK_DONE);
if (todo & T_FORK_A) assert(msg == cpid);
}
if (todo & T_SIGNAL) {
assert(0 == ptrace(PTRACE_CONT, pid, 0, 0));
assert(waitpid(pid, &stat, __WALL) == pid);
if (!(todo & T_FORK_A) && stat == 0x4300)
goto parent_exited_first;
assert(WIFSTOPPED(stat));
assert(WSTOPSIG(stat) == SIGCHLD);
}
if (todo & T_SYSCALL) {
msg = step(pid, PTRACE_SYSCALL, 0);
if (todo & T_FORK_A) assert(msg == cpid);
}
assert(0 == ptrace(PTRACE_CONT, pid, 0, 0));
assert(waitpid(pid, &stat, __WALL) == pid);
assert(WIFEXITED(stat));
parent_exited_first:
assert(waitpid(-1, &stat, __WALL) == -1);
assert(errno == ECHILD);
}
int main(int argc, char* argv[])
{
int todo;
if (argc > 1) {
todo = atoi(argv[1]);
tst(todo);
} else {
int todo;
for (todo = 0; todo <= T_ALL; ++todo)
tst(todo);
}
return 0;
}
---
kernel/ptrace.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 48 insertions(+), 2 deletions(-)
--- PU/kernel/ptrace.c~31_TRACEVFORKDONE 2009-09-12 01:04:59.000000000 +0200
+++ PU/kernel/ptrace.c 2009-09-13 21:22:24.000000000 +0200
@@ -32,6 +32,8 @@ struct ptrace_context {
int stopped_code;
resume_stopped_t resume_stopped;
+
+ bool o_did_clone_vfork; // XXX: move into ->options
};
static inline struct ptrace_context *
@@ -56,6 +58,8 @@ void __ptrace_link(struct task_struct *c
static const struct utrace_engine_ops ptrace_utrace_ops; /* forward decl */
static int ptrace_attach_task(struct task_struct *tracee, int options);
static void ptrace_abort_attach(struct task_struct *tracee);
+static void ptrace_wake_up(struct utrace_engine *engine,
+ struct task_struct *tracee, enum utrace_resume_action action);
static void ptrace_detach_task(struct task_struct *child, int sig)
{
@@ -221,6 +225,35 @@ static void ptrace_clone_attach(struct t
set_tsk_thread_flag(child, TIF_SIGPENDING);
}
+static void ptrace_resume_vfork_done(struct utrace_engine *engine,
+ struct task_struct *tracee, long data)
+{
+ ptrace_wake_up(engine, tracee, UTRACE_RESUME);
+}
+
+static bool ck_o_tracevforkdone(struct utrace_engine *engine,
+ struct task_struct *tracee)
+{
+ struct ptrace_context *context = ptrace_context(engine);
+
+ if (!(context->o_did_clone_vfork &&
+ (context->options & PTRACE_O_TRACEVFORKDONE)))
+ return false;
+
+ context->resume_stopped = ptrace_resume_vfork_done;
+ context->stopped_code = (PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP;
+
+ utrace_control(tracee, engine, UTRACE_REPORT);
+ return true;
+}
+
+static void ptrace_resume_clone(struct utrace_engine *engine,
+ struct task_struct *tracee, long data)
+{
+ ck_o_tracevforkdone(engine, tracee);
+ ptrace_wake_up(engine, tracee, UTRACE_RESUME);
+}
+
static u32 ptrace_report_clone(enum utrace_resume_action action,
struct utrace_engine *engine,
struct task_struct *parent,
@@ -230,11 +263,16 @@ static u32 ptrace_report_clone(enum utra
struct ptrace_context *context = ptrace_context(engine);
int event;
+ WARN_ON(context->resume_stopped);
+ WARN_ON(context->stopped_code);
+
if (clone_flags & CLONE_UNTRACED)
return UTRACE_RESUME;
event = 0;
+ context->o_did_clone_vfork = false;
if (clone_flags & CLONE_VFORK) {
+ context->o_did_clone_vfork = true;
if (context->options & PTRACE_O_TRACEVFORK)
event = PTRACE_EVENT_VFORK;
} else if ((clone_flags & CSIGNAL) != SIGCHLD) {
@@ -251,8 +289,16 @@ static u32 ptrace_report_clone(enum utra
if (event || (clone_flags & CLONE_PTRACE))
ptrace_clone_attach(parent, child, context->options);
- if (event)
- return utrace_ptrace_event(parent, event, child->pid);
+ // XXX: child->pid is wrong! use tracer's pid_ns
+ if (event) {
+ parent->ptrace_message = child->pid;
+ context->resume_stopped = ptrace_resume_clone;
+ context->stopped_code = (event << 8) | SIGTRAP;
+
+ return UTRACE_STOP;
+ } else if (ck_o_tracevforkdone(engine, parent)) {
+ parent->ptrace_message = child->pid;
+ }
return UTRACE_RESUME;
}
More information about the utrace-devel
mailing list