new(ptrace): Ptrace child, which ptraces its own child.
authorsgf <sgf.dma@gmail.com>
Wed, 22 Nov 2023 14:34:47 +0000 (17:34 +0300)
committersgf <sgf.dma@gmail.com>
Tue, 5 Dec 2023 20:57:50 +0000 (23:57 +0300)
containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c [new file with mode: 0644]

diff --git a/containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c b/containers_from_scratch/guide_to_syscalls/myStrace/ptrace-chain.c
new file mode 100644 (file)
index 0000000..ef8d9e7
--- /dev/null
@@ -0,0 +1,256 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <ctype.h>
+
+// 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 void a(){
+  while(1){
+    printf ("A\n");
+    fflush(stdout);
+    sleep(1);
+  }
+}
+
+static void b(){
+  while(1){
+    printf ("B\n");
+    fflush(stdout);
+    sleep(1);
+  }
+}
+
+static void c(){
+  while(1){
+    printf ("C\n");
+    fflush(stdout);
+    sleep(1);
+  }
+}
+
+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);
+
+    if (WIFEXITED(status)) {
+        printf("%s: Child exited with status=%d\n", pref, WEXITSTATUS(status));
+        child_pid = 0;
+        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;
+        raise(SIGINT);
+
+    } else if (WIFSTOPPED(status)) {
+        int csig = WSTOPSIG(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);
+
+        printf("%s: Child is traced and and is about to receive '%s' (%d)\n", pref, sys_siglist[csig], csig);
+
+        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 (csiginfo.si_signo == SIGTRAP && csiginfo.si_code == SIGTRAP|0x80) {
+                    printf("%s: Tracee's syscall-stop confirmed (SYSGOOD)\n", pref);
+                }
+
+                break;
+
+            case SIGTRAP:
+                printf("%s: Tracee trapped\n", pref);
+                switch (status>>8) {
+                    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);
+                        break;
+
+                    default:
+                        ret = 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);
+                        } else if (csiginfo.si_code == SI_KERNEL) {
+                            printf("%s: Tracee is in signal-delivery stop: received SIGTRAP from kernel\n", pref);
+                        } else if (csiginfo.si_code == SIGTRAP) {
+                            printf("%s: Tracee is in syscall-stop\n", pref);
+                        } else {
+                            printf("%s: Tracee is in some other trap..\n", pref);
+                        }
+
+                        break;
+                }
+
+                break;
+
+            case SIGSTOP:
+            case SIGTSTP:
+            case SIGTTIN:
+            case SIGTTOU:
+                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);
+                } else {
+                    printf("%s: Tracee is in STOP signal signal-delivery-stop, injecting stop signal %d\n", pref, csig);
+                    ptrace(PTRACE_CONT, 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);
+                break;
+        }
+
+    } else if (WIFCONTINUED(status)) {
+        printf("%s: Child continued\n", pref);
+
+    } else {
+        printf("%s: Child is elsewhere\n", pref);
+    }
+
+    free(pref);
+}
+
+// FIXME: Try sigaction with SA_NOCLDSTOP .
+static void catcher(int sig, siginfo_t *info, void *vp) {
+    pid_t mypid = getpid();
+
+    size_t n_pref = 1 + snprintf(NULL, 0, "[%d] (%d):", mypid, sig);
+    char *pref = malloc(n_pref);
+
+    snprintf(pref, n_pref, "[%d] (%d)", mypid, sig);
+
+    printf("%s: Received signal '%s' (%d) on %d (reason %d)\n", pref, sys_siglist[sig], sig, info->si_pid, info->si_code);
+
+    switch (sig) {
+        case SIGINT:
+            if (child_pid != 0) {
+                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);
+            }
+            break;
+
+        case SIGTRAP:
+            printf("%s: We're trapped\n", pref);
+            break;
+
+        case SIGCHLD: {
+            // *info - info about signal received by _parent_.
+            switch (info->si_code) {
+                case CLD_EXITED:
+                    printf("%s: SIGCHLD: Child has exited with %d\n", pref, info->si_status);
+                    break;
+                case CLD_KILLED:
+                    printf("%s: SIGCHLD: Child was killed by '%s' (%d).\n", pref, sys_siglist[info->si_status], info->si_status);
+                    break;
+                case CLD_DUMPED:
+                      printf("%s: SIGCHLD: Child terminated abnormally.\n", pref);
+                      break;
+                case CLD_TRAPPED:
+                      printf("%s: SIGCHLD: Traced child has trapped by '%s' (%d)\n", pref, sys_siglist[info->si_status], info->si_status);
+                      break;
+                case CLD_STOPPED:
+                      printf("%s: SIGCHLD: Child has stopped by '%s' (%d)\n", pref, sys_siglist[info->si_status], info->si_status);
+                      break;
+                case CLD_CONTINUED:
+                      printf("%s: SIGCHLD: Stopped child has continued by '%s' (%d).\n", pref, sys_siglist[info->si_status], info->si_status);
+                      break;
+            }
+
+            int wstatus;
+            pid_t child_pid;
+            while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
+                printf("%s: Child pid %d\n", pref, child_pid);
+
+                child_has_stopped(child_pid, wstatus);
+            }
+        }
+    }
+    printf("%s: --\n", pref);
+}
+
+int main(int  argc,
+         char **argv){
+
+    printf("pidA = %d\n", getpid());
+
+    struct sigaction sa;
+    sa.sa_sigaction = catcher;
+    sa.sa_flags = SA_SIGINFO;
+    sigemptyset(&sa.sa_mask);
+    sigaction(SIGCHLD, &sa, NULL);
+    sigaction(SIGTRAP, &sa, NULL);
+    sigaction(SIGINT, &sa, NULL);
+
+    int   result;
+
+    printf("Forking B\n");
+    child_pid = fork();
+    if (child_pid) {
+        printf("pidB = %d\n", child_pid);
+
+        sleep(1);
+        printf("[%d]: attaching to B..\n", getpid());
+        result = ptrace(PTRACE_ATTACH, child_pid, NULL, NULL);
+        if(result == -1){
+            perror("outer ptrace");
+        }
+
+        a();
+
+    } else {
+        printf("B Started\n");
+        sleep(3);
+        printf("Forking C\n");
+        child_pid = fork();
+
+        //b();
+        if (child_pid) {
+            printf("pidC = %d\n", child_pid);
+
+            printf("[%d]: attaching to pidC..\n", getpid());
+            result = ptrace(PTRACE_ATTACH, child_pid, NULL, NULL);
+            if(result==-1){
+                perror("inner ptrace");
+            }
+
+            b();
+
+        } else {
+            printf("C Started\n");
+            c();
+        }
+    }
+
+    return 0;
+}