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, &regs));
        }

        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);

Reply via email to