chg(ptrace): Trace syscalls. Spawn any number of childs.
authorsgf <sgf.dma@gmail.com>
Thu, 23 Nov 2023 14:53:11 +0000 (17:53 +0300)
committersgf <sgf.dma@gmail.com>
Tue, 5 Dec 2023 20:57:51 +0000 (23:57 +0300)
containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c

index ef8d9e7..e52f0c2 100644 (file)
@@ -7,14 +7,44 @@
 #include <sys/wait.h>
 #include <errno.h>
 #include <ctype.h>
+#include <stdbool.h>
 
+// 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;
 }