From 6fe8252bcf2273a88526c63baffef86e13a296b7 Mon Sep 17 00:00:00 2001 From: sgf Date: Thu, 23 Nov 2023 17:53:11 +0300 Subject: [PATCH] chg(ptrace): Trace syscalls. Spawn any number of childs. --- .../guide_to_syscalls/myStrace/ptrace-chain.c | 201 +++++++++++++++--- 1 file changed, 171 insertions(+), 30 deletions(-) diff --git a/containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c b/containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c index ef8d9e7..e52f0c2 100644 --- a/containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c +++ b/containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c @@ -7,14 +7,44 @@ #include #include #include +#include +// Spawn and trace child process, which spawn and trace its own child. +// +// Example taken from here // https://stackoverflow.com/questions/2359581/calling-ptrace-inside-a-ptraced-linux-process // https://stackoverflow.com/questions/18437779/do-i-need-to-do-anything-with-a-sigchld-handler-if-i-am-just-using-wait-to-wai extern const char * const sys_siglist[]; -static pid_t child_pid; +static pid_t child_pid = 0; +// Whether SIGSTOP send to tracee right after PTRACE_ATTACH was seen or not. +static bool attach_stop = false; + +// Is tracee now inside syscall (syscall-enter-stop was seen) or outside +// (the last syscall-stop was syscall-exit-stop). +static bool in_syscall = false; + +// How to restart ptrace-stopped tracee. +const enum __ptrace_request ptrace_restart = PTRACE_SYSCALL; +//const enum __ptrace_request ptrace_restart = PTRACE_CONT; + +// Representation of each process. +typedef struct worker { + char *name; + void (*work)(char*); +} Worker; + +static void f(char *s){ + while(1){ + printf ("%s\n", s); + fflush(stdout); + sleep(1); + } +} + +/* static void a(){ while(1){ printf ("A\n"); @@ -38,22 +68,36 @@ static void c(){ sleep(1); } } +*/ +// Will start following workers. +static const Worker workers[] = { + {.name = "A", .work = f}, + {.name = "B", .work = f}, + //{.name = "C", .work = f}, +}; +static const size_t n_workers = sizeof(workers)/sizeof(Worker); + +// Child was stopped by ptrace. Determine why and restart it properly. static void child_has_stopped(pid_t cpid, int status) { size_t n_pref = 1 + snprintf(NULL, 0, "[Child %d]:", cpid); char *pref = malloc(n_pref); - snprintf(pref, n_pref, "[Child %d]:", cpid); + snprintf(pref, n_pref, "[Child %d]", cpid); if (WIFEXITED(status)) { printf("%s: Child exited with status=%d\n", pref, WEXITSTATUS(status)); child_pid = 0; + // My child exited, so should i. + free(pref); raise(SIGINT); } else if (WIFSIGNALED(status)) { int termsig = WTERMSIG(status); printf("%s: Child killed by signal '%s' (%d)\n", pref, sys_siglist[termsig], termsig); child_pid = 0; + // My child was killed, so i'm quitting now. + free(pref); raise(SIGINT); } else if (WIFSTOPPED(status)) { @@ -61,45 +105,89 @@ static void child_has_stopped(pid_t cpid, int status) { n_pref = 1 + snprintf(pref, n_pref, "[Child %d] (%d):", cpid, csig); pref = realloc(pref, n_pref); - snprintf(pref, n_pref, "[Child %d] (%d):", cpid, csig); + snprintf(pref, n_pref, "[Child %d] (%d)", cpid, csig); - printf("%s: Child is traced and and is about to receive '%s' (%d)\n", pref, sys_siglist[csig], csig); + if (csig == SIGTRAP|0x80) { + printf("%s: Child is traced and and is about to receive '%s' (%d|0x80 = %d)\n", pref, sys_siglist[csig^0x80], csig^0x80, csig); + } else { + printf("%s: Child is traced and and is about to receive '%s' (%d)\n", pref, sys_siglist[csig], csig); + } + siginfo_t csiginfo; switch (csig) { case SIGTRAP|0x80: - printf("%s: Tracee is in syscall-stop (SYSGOOD)\n", pref); - siginfo_t csiginfo; - int ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); + if (!in_syscall) { + printf("%s: TRAP|0x80: Tracee is in syscall-ENTER-stop\n", pref); + } else { + printf("%s: TRAP|0x80: Tracee is in syscall-EXIT-stop\n", pref); + } + + /* + ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); if (csiginfo.si_signo == SIGTRAP && csiginfo.si_code == SIGTRAP|0x80) { - printf("%s: Tracee's syscall-stop confirmed (SYSGOOD)\n", pref); + printf("%s: TRAP|0x80: Tracee's syscall-stop confirmed\n", pref); } + */ + + in_syscall = !in_syscall; + ptrace(ptrace_restart, cpid, 0, 0); + break; case SIGTRAP: - printf("%s: Tracee trapped\n", pref); switch (status>>8) { + case ((PTRACE_EVENT_EXIT<<8) | SIGTRAP): + if (ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo) == -1) { + perror("PTRACE_GETSIGINFO:"); + abort(); + } + + unsigned long exit_status; + if (ptrace(PTRACE_GETEVENTMSG, cpid, 0, &exit_status) == -1) { + perror("PTRACE_GETEVENTMSG:"); + abort(); + } + + exit_status >>= 8; + printf("%s: TRAP: Tracee is in ptrace-event stop after EXIT with %d (si_code = %d, status = %d)\n", pref, exit_status, csiginfo.si_code, status>>8); + ptrace(ptrace_restart, cpid, 0, 0); + break; + case ((PTRACE_EVENT_VFORK<<8) | SIGTRAP): case ((PTRACE_EVENT_FORK<<8) | SIGTRAP): case ((PTRACE_EVENT_CLONE<<8) | SIGTRAP): case ((PTRACE_EVENT_VFORK_DONE<<8) | SIGTRAP): case ((PTRACE_EVENT_EXEC<<8) | SIGTRAP): - case ((PTRACE_EVENT_EXIT<<8) | SIGTRAP): case ((PTRACE_EVENT_STOP<<8) | SIGTRAP): - ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); - printf("%s: Tracee is in ptrace-event stop (si_code = %d)\n", pref, csiginfo.si_code); + ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); + // PTRACE_GETEVENTMSG . + printf("%s: TRAP: Tracee is in some ptrace-event stop (si_code = %d)\n", pref, csiginfo.si_code); + ptrace(ptrace_restart, cpid, 0, 0); break; default: - ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); + ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); if (csiginfo.si_code <= 0) { - printf("%s: Tracee is in signal-delivery stop: received SIGTRAP from user-space\n", pref); + printf("%s: TRAP: Tracee is in signal-delivery stop: received from user-space\n", pref); + ptrace(ptrace_restart, cpid, 0, csig); + } else if (csiginfo.si_code == SI_KERNEL) { - printf("%s: Tracee is in signal-delivery stop: received SIGTRAP from kernel\n", pref); + printf("%s: TRAP: Tracee is in signal-delivery stop: received from kernel\n", pref); + ptrace(ptrace_restart, cpid, 0, csig); + } else if (csiginfo.si_code == SIGTRAP) { - printf("%s: Tracee is in syscall-stop\n", pref); + if (!in_syscall) { + printf("%s: TRAP: Tracee is in syscall-ENTER-stop\n", pref); + } else { + printf("%s: TRAP: Tracee is in syscall-EXIT-stop\n", pref); + } + in_syscall = !in_syscall; + ptrace(ptrace_restart, cpid, 0, 0); + } else { - printf("%s: Tracee is in some other trap..\n", pref); + printf("%s: TRAP: Tracee is in some other trap..\n", pref); + ptrace(ptrace_restart, cpid, 0, 0); } break; @@ -111,20 +199,36 @@ static void child_has_stopped(pid_t cpid, int status) { case SIGTSTP: case SIGTTIN: case SIGTTOU: - ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); + ; + int ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo); if (ret == -1 && errno == EINVAL) { - printf("%s: Tracee is in group-stop, ignoring stop signal %d\n", pref, csig); - ptrace(PTRACE_CONT, cpid, 0, 0); + printf("%s: STOP: Tracee is in group-stop (%s), ignoring stop signal %d\n", pref, strerror(errno), csig); + + ptrace(ptrace_restart, cpid, 0, 0); + } else { - printf("%s: Tracee is in STOP signal signal-delivery-stop, injecting stop signal %d\n", pref, csig); - ptrace(PTRACE_CONT, cpid, 0, csig); + printf("%s: STOP: Tracee is in signal-delivery-stop, injecting stop signal %d\n", pref, csig); + + if (!attach_stop) { + printf("%s: STOP: stop signal after attach\n", pref); + attach_stop = true; + printf("%s: STOP: Enabling event tracing\n", pref); + //if(ptrace(PTRACE_SETOPTIONS, child_pid, NULL, PTRACE_O_TRACEEXIT) == -1) { + if(ptrace(PTRACE_SETOPTIONS, child_pid, NULL, + PTRACE_O_TRACEEXIT | PTRACE_O_TRACESYSGOOD) == -1) { + perror("ptrace setopts"); + abort(); + } + } + + ptrace(ptrace_restart, cpid, 0, csig); } break; default: printf("%s: Tracee is in signal-delivery-stop, injecting signal %d\n", pref, csig); - ptrace(PTRACE_CONT, cpid, 0, csig); + ptrace(ptrace_restart, cpid, 0, csig); break; } @@ -155,8 +259,9 @@ static void catcher(int sig, siginfo_t *info, void *vp) { printf("%s: SIGINT: Resend to child %d\n", pref, child_pid); kill(child_pid, SIGINT); } else { - printf("%s: SIGINT: No child left, exit\n", pref); - _exit(123); + printf("%s: SIGINT: No child left, exit with %d\n", pref, mypid%100); + free(pref); + _exit(mypid%100); } break; @@ -197,12 +302,13 @@ static void catcher(int sig, siginfo_t *info, void *vp) { } } printf("%s: --\n", pref); + + free(pref); } -int main(int argc, - char **argv){ +int main(int argc, char **argv){ - printf("pidA = %d\n", getpid()); + setvbuf(stdout, NULL, _IONBF, 0); struct sigaction sa; sa.sa_sigaction = catcher; @@ -210,10 +316,43 @@ int main(int argc, sigemptyset(&sa.sa_mask); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGTRAP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); // Ctrl+C + + if (n_workers < 1) { + printf("Nikogo netu doma\n"); + abort(); + } + + int i = 0; + char *myname = workers[i].name; + void (*mywork) (char*) = workers[i].work; + printf("pid%s = %d\n", myname, getpid()); + + for (++i; i < n_workers; i++) { + char *childname = workers[i].name; + printf("Forking %s\n", childname); + child_pid = fork(); + if (child_pid) { + printf("pid%s = %d\n", childname, child_pid); + + sleep(1); + printf("[%d]: attaching to %s..\n", getpid(), childname); + if(ptrace(PTRACE_ATTACH, child_pid, NULL, NULL) == -1){ + perror("PTRACE_ATTACH"); + } + + break; + + } else { + myname = workers[i].name; + mywork = workers[i].work; + printf("%s: My pid %d\n", myname, getpid()); + } + } - int result; + mywork(myname); + /* printf("Forking B\n"); child_pid = fork(); if (child_pid) { @@ -242,6 +381,7 @@ int main(int argc, result = ptrace(PTRACE_ATTACH, child_pid, NULL, NULL); if(result==-1){ perror("inner ptrace"); + abort(); } b(); @@ -251,6 +391,7 @@ int main(int argc, c(); } } + */ return 0; } -- 2.20.1