utrace-ptrace can't rely on syscall_trace_leave()->send_sigtrap(), we should generate this trap by hand.
To do this we have to split PTRACE_EVENT_SYSCALL into PTRACE_EVENT_SYSCALL_ENTRY and PTRACE_EVENT_SYSCALL_EXIT to identify the latter case which needs a trap. Note! I think we can avoid using send_sigtrap(). Instead we can rely on ptrace_report_sinal() which can report SIGTRAP. But this should be discussed beacuse of some ptrace oddities here. Now we pass all tests except these two attach-wait-on-stopped attach-into-signal which should fail. Plus the kernel passes this test-case: #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> #include <sys/user.h> #include <sys/debugreg.h> #include <sys/syscall.h> #define WEVENT(s) ((s & 0xFF0000) >> 16) static int verbose; #define d_printf(fmt, ...) do { if (verbose) printf(fmt, ##__VA_ARGS__); } while (0) static struct user_regs_struct regs; static void resume(int pid, int req, int ck_stat) { int stat; assert(0 == ptrace(req, pid, 0, 0)); assert(waitpid(pid, &stat, __WALL) == pid); //d_printf("===> %06X\n %06X\n", ck_stat, stat); assert(stat == ck_stat); assert(0 == ptrace(PTRACE_GETREGS, pid, NULL, ®s)); } int main(int argc, const char *argv[]) { int pid, child, stat; long rip, nxt_rip; if (getpid() == __NR_getppid) { printf("sorry, restart\n"); return 0; } verbose = argc > 1; pid = fork(); if (!pid) { assert(0 == ptrace(PTRACE_TRACEME, 0,0,0)); kill(getpid(), SIGSTOP); // 1: SYSCALL + SYSCALL + STEP getppid(); // 2: SYSCALL + STEP getppid(); // 3: STEP getppid(); // 4: SYSCALL + STEP if (!fork()) exit(73); // STEPs only if (!fork()) exit(73); assert(0); } assert(wait(&stat) == pid); assert(WIFSTOPPED(stat) && WSTOPSIG(stat) == SIGSTOP); assert(0 == ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK)); //----------------------------------------------------------------------------- d_printf("1: syscall enter\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == -ENOSYS); rip = regs.rip; d_printf("1: syscall leave\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("1: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("1: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.rip != rip); //-------------------------------------------------------------------------------- d_printf("2: stop before syscall insn\n"); do { resume(pid, PTRACE_SINGLESTEP, 0x57F); } while (regs.rip != rip - 2); assert(regs.rax == __NR_getppid); d_printf("2: syscall enter\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == -ENOSYS); assert(regs.rip == rip); d_printf("2: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("2: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.rip != rip); //-------------------------------------------------------------------------------- d_printf("3: stop before syscall insn\n"); do { resume(pid, PTRACE_SINGLESTEP, 0x57F); } while (regs.rip != rip - 2); assert(regs.rax == __NR_getppid); d_printf("3: step over syscall\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("3: step over syscall\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.rip != rip); //---------------------------------------------------------------------------------- d_printf("4: syscall enter\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_clone); assert(regs.rax == -ENOSYS); rip = regs.rip; child = waitpid (-1, &stat, WNOHANG); assert(child == 0); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F | (PTRACE_EVENT_FORK << 16)); assert(regs.orig_rax == __NR_clone); //assert(regs.rax == -ENOSYS); assert(regs.rip == rip); child = waitpid (-1, &stat, __WALL); assert(child > 0 && child != pid); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.orig_rax == __NR_clone); assert(regs.rax == child); assert(regs.rip == rip); d_printf("4: provoke SIGCHLD\n"); kill(child, SIGKILL); assert(child == waitpid (-1, &stat, __WALL)); assert(stat = SIGKILL); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x7F | (SIGCHLD << 8)); assert(regs.rip == rip); d_printf("4: SIGTERM + STEP\n"); kill(pid, SIGTERM); resume(pid, PTRACE_SINGLESTEP, 0x7F | (SIGTERM << 8)); assert(regs.rip == rip); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.rip != rip); nxt_rip = regs.rip; //--------------------------------------------------------------------------- d_printf("5: stop before syscall insn\n"); do { resume(pid, PTRACE_SINGLESTEP, 0x57F); } while (regs.rip != rip - 2); assert(regs.rax == __NR_clone); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F | (PTRACE_EVENT_FORK << 16)); assert(regs.orig_rax == __NR_clone); //assert(regs.rax == -ENOSYS); assert(regs.rip == rip); child = waitpid (-1, &stat, __WALL); assert(child > 0 && child != pid); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.orig_rax == __NR_clone); assert(regs.rax == child); assert(regs.rip == rip); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.rip == nxt_rip); kill(child, SIGKILL); assert(child == waitpid (-1, &stat, __WALL)); assert(stat = SIGKILL); d_printf("5: step\n"); resume(pid, PTRACE_CONT, 0x7F | (SIGCHLD << 8)); assert(regs.rip == nxt_rip); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.rip != nxt_rip); //---------------------------------------------------------------------------- kill(pid, SIGKILL); return 0; } --- kernel/ptrace.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) --- PU/kernel/ptrace.c~56_SYSCALL_TRAP 2009-10-01 23:32:55.000000000 +0200 +++ PU/kernel/ptrace.c 2009-10-02 00:16:57.000000000 +0200 @@ -36,9 +36,10 @@ struct ptrace_context { enum utrace_resume_action resume; }; -#define PTRACE_EVENT_SYSCALL 100 -#define PTRACE_EVENT_SIGTRAP 101 -#define PTRACE_EVENT_SIGNAL 102 +#define PTRACE_EVENT_SYSCALL_ENTRY 100 +#define PTRACE_EVENT_SYSCALL_EXIT 101 +#define PTRACE_EVENT_SIGTRAP 102 +#define PTRACE_EVENT_SIGNAL 103 static inline bool ev_pending(struct ptrace_context *context) { @@ -214,7 +215,7 @@ static u32 ptrace_report_syscall_entry(u WARN_ON(ev_pending(context)); - context->ev_name = PTRACE_EVENT_SYSCALL; + context->ev_name = PTRACE_EVENT_SYSCALL_ENTRY; context->ev_code = (context->options & PTRACE_O_TRACESYSGOOD) ? (SIGTRAP | 0x80) : SIGTRAP; return UTRACE_SYSCALL_RUN | UTRACE_STOP; @@ -230,7 +231,7 @@ static u32 ptrace_report_syscall_exit(en if (ev_pending(context)) return UTRACE_STOP; - context->ev_name = PTRACE_EVENT_SYSCALL; + context->ev_name = PTRACE_EVENT_SYSCALL_EXIT; context->ev_code = (context->options & PTRACE_O_TRACESYSGOOD) ? (SIGTRAP | 0x80) : SIGTRAP; return UTRACE_STOP; @@ -882,7 +883,8 @@ static void do_ptrace_resume(struct utra case 0: // XXX: JCTL stop break; - case PTRACE_EVENT_SYSCALL: + case PTRACE_EVENT_SYSCALL_ENTRY: + case PTRACE_EVENT_SYSCALL_EXIT: if (data) send_sig_info(data, SEND_SIG_PRIV, tracee); break; @@ -897,7 +899,7 @@ static void do_ptrace_resume(struct utra case PTRACE_EVENT_FORK: case PTRACE_EVENT_CLONE: case PTRACE_EVENT_VFORK_DONE: - context->ev_name = PTRACE_EVENT_SYSCALL; + context->ev_name = PTRACE_EVENT_SYSCALL_EXIT; context->ev_code = (context->options & PTRACE_O_TRACESYSGOOD) ? (SIGTRAP | 0x80) : SIGTRAP; do_ptrace_notify_stop(context, tracee); @@ -905,6 +907,22 @@ static void do_ptrace_resume(struct utra } } + if (action != UTRACE_RESUME) { + switch (context->ev_name) { + case PTRACE_EVENT_EXEC: + case PTRACE_EVENT_FORK: + case PTRACE_EVENT_CLONE: + case PTRACE_EVENT_SYSCALL_EXIT: + read_lock(&tasklist_lock); + if (tracee->sighand) + send_sigtrap(tracee, task_pt_regs(tracee), 0, TRAP_BRKPT); + read_unlock(&tasklist_lock); + + action = UTRACE_RESUME; + break; + } + } + context->ev_name = 0; context->resume = action; ptrace_wake_up(engine, tracee, action);