This needs more comment, I'll try to add them in a separate patch.

With the recent changes in utrace API utrace_control(UTRACE_SINGLESTEP)
postpones enable_step() to utrace_resume() stage. The tracee can pass
tracehook_report_syscall_exit() without TIF_SINGLESTEP, this breaks
the send_sigtrap() logic.

Change ptrace_resume_action() to set UTRACE_EVENT(SYSCALL_EXIT) in
PTRACE_SINGLESTEP/PTRACE_SINGLEBLOCK case, change report_syscall_exit()
to check ctx->resume to detect this case. To synthesize the trap we
set ctx->signr = SIGTRAP and provoke UTRACE_SIGNAL_REPORT, like
ptrace_resume() does.

Roland, we already discussed this a bit and I agree, this is not optimal.
We need some changes in utrace, but currently it has a lot of problems
and it is not clear yet what utrace should do. Let's fix the code now,
once we find a better solution we can revert this change.

With this patch "make check" passes all tests again (to clarify, except
some tests which upstream doesn't pass too), including this one:
        
        #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((long)regs.orig_rax < 0);
                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 |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

--- UTRACE-PTRACE/kernel/ptrace.c~133_SYSCALL_EXIT_TRAP 2009-11-16 
00:04:22.000000000 +0100
+++ UTRACE-PTRACE/kernel/ptrace.c       2009-11-16 16:59:48.000000000 +0100
@@ -436,8 +436,16 @@ static u32 ptrace_report_syscall_exit(en
        if (ptrace_event_pending(ctx))
                return UTRACE_STOP;
 
-       set_syscall_code(ctx, PTRACE_EVENT_SYSCALL_EXIT);
+       if (ctx->resume != UTRACE_RESUME) {
+               WARN_ON(ctx->resume != UTRACE_BLOCKSTEP &&
+                       ctx->resume != UTRACE_SINGLESTEP);
+               ctx->resume = UTRACE_RESUME;
+
+               ctx->signr = SIGTRAP;
+               return UTRACE_INTERRUPT;
+       }
 
+       set_syscall_code(ctx, PTRACE_EVENT_SYSCALL_EXIT);
        return UTRACE_STOP;
 }
 
@@ -879,6 +887,7 @@ static int ptrace_resume_action(struct t
                if (unlikely(!arch_has_block_step()))
                        return -EIO;
                action = UTRACE_BLOCKSTEP;
+               events |= UTRACE_EVENT(SYSCALL_EXIT);
                break;
 #endif
 
@@ -887,6 +896,7 @@ static int ptrace_resume_action(struct t
                if (unlikely(!arch_has_single_step()))
                        return -EIO;
                action = UTRACE_SINGLESTEP;
+               events |= UTRACE_EVENT(SYSCALL_EXIT);
                break;
 #endif
 

Reply via email to