Sorry for delay, I was offline 3 days.

On 09/25, Oleg Nesterov wrote:
>
> IOW, every time we need to check whether we should generate/report
> the artificial SYSCALL_EXIT stop event, we must also check whether
> we need send_sigtrap().
>
> With this patch we pass the new step-from-clone.c test.
>       
> Further changes:
>
>       - I guess, we should also send SIGTRAP when SINGLESTEP resumes
>         the tracee from SYSCALL_EXIT stop. This is trivial, but I need
>         to write the test-case first.

Yes, with this change the kernel passes all tests plus the test-case
below. I wrote it because I lost the hope to understand the required
behaviour ;)

I'll send the patch tomorrow (hopefully I'll also send ->siginfo changes).

The last commit in your utrace-ptrace branch is

        implement UTRACE_SIGNAL_HANDLER stepping
        4492770dc8d2312da9518e8b85fb0e49dc3da510

OK, I'll drop the changes I did since then, and redo/resend.


Also. When I did the testing, I noticed the test-case below runs much,
much slower compared to vanilla kernel. It took me a lot of time to
understand that the reason is simple, do_ptrace_notify_stop() needs
the trivial backport of 53da1d9456fe7f87a920a78fdbdcf1225d197cb7.

I still wonder though, why this makes such a noticeable difference.
The slowdown mostly comes from

        do {
                resume(pid, PTRACE_SINGLESTEP, 0x57F);
        } while (regs.rip != rip - 2);

loop which does only 70 iterations. But I am not going to dig further.


Note also the couple of "assert(regs.rax == -ENOSYS)" which were commented
out, here we differ from vanilla kernel and this can't be "fixed". But,
as you pointed out previously, we do not need to be bug-compatible here.

Oleg.

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

                // 5: 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;
}

Reply via email to