--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdbool.h>
+
+int main(int argc, char **argv){
+ setvbuf(stdout, NULL, _IONBF, 0);
+ if (argc < 2) {
+ printf("Too few arguments\n");
+ abort();
+ }
+
+ pid_t mypid = getpid();
+ printf("pidA = %d\n", mypid);
+
+ char *cmd = argv[1];
+ char **newargv = malloc(sizeof(char*) * argc);
+ for (int i = 1; i < argc; i++) {
+ newargv[i-1] = argv[i];
+ }
+ newargv[argc-1] = NULL;
+
+ pid_t child_pid = fork();
+ if (child_pid) {
+ printf("[%d]: pidB = %d\n", mypid, child_pid);
+
+ if (kill(child_pid, 0) == -1) {
+ perror("signal right after start: ");
+ } else {
+ printf("[%d]: Successfully sent a signal right after start\n", mypid);
+ }
+
+ struct user_regs_struct regs;
+ printf("[%d]: Get child registers\n", mypid);
+ // Following register quire will fail in any case, because child is
+ // not stopped!
+ if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®s) == -1) {
+ perror("PTRACE_GETREGS:");
+ }
+ printf("[%d]: orig_rax = %d\n", mypid, regs.orig_rax);
+
+ printf("[%d]: waiting for child to stop\n", mypid);
+ int status;
+ while (waitpid(child_pid, &status, 0)) {
+ if (WIFEXITED(status)) {
+ printf("[%d]: Child exited with status %d\n", mypid, WEXITSTATUS(status));
+ return 0;
+
+ } else if (WIFSIGNALED(status)) {
+ int termsig = WTERMSIG(status);
+ printf("[%d]: Child killed by signal %d\n", mypid, termsig);
+ return 0;
+
+ } else if (WIFSTOPPED(status)) {
+ int csig = WSTOPSIG(status);
+ printf("[%d]: Child is traced and and is about to receive signal %d\n", mypid, csig);
+
+ if (kill(child_pid, 0) == -1) {
+ perror("signal when stopped: ");
+ } else {
+ printf("[%d]: Successfully sent a signal when stopped\n", mypid);
+ }
+
+ sleep(2);
+ struct user_regs_struct regs;
+ printf("[%d]: Get child registers\n", mypid);
+ // But this register will succeed, because now child is
+ // stopped.
+ if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®s) == -1) {
+ perror("PTRACE_GETREGS:");
+ }
+ printf("[%d]: orig_rax = %d\n", mypid, regs.orig_rax);
+
+ sleep(1);
+ // Suppress signal.
+ //if (ptrace(PTRACE_CONT, child_pid, 0, 0) == 1) {
+ // perror("PTRACE_CONT:");
+ //}
+ if (ptrace(PTRACE_SYSCALL, child_pid, 0, 0) == 1) {
+ perror("PTRACE_SYSCALL:");
+ }
+
+ if (kill(child_pid, 0) == -1) {
+ perror("signal when running: ");
+ } else {
+ printf("[%d]: Successfully sent a signal when running\n", mypid);
+ }
+
+
+ } else if (WIFCONTINUED(status)) {
+ printf("[%d]: Child continued\n", mypid);
+
+ } else {
+ printf("[%d]: Child is elsewhere\n", mypid);
+ }
+ }
+
+ } else {
+ mypid = getpid();
+ if ((ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) == -1) {
+ perror("PTRACE_TRACEME:");
+ }
+ // Child should normally raise SIGSTOP, so parent has a chance to
+ // inspect its state through ptrace.
+ // But go runtime does not raise such signal.
+ //raise(SIGSTOP);
+
+ printf("[%d]: Sleep after traceme and before execve..\n", mypid);
+ sleep(6);
+ printf("[%d]: execve '%s'\n", mypid, cmd);
+ execve(cmd, newargv, NULL);
+ perror("execve");
+ }
+ return 0;
+}
--- /dev/null
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "syscall"
+ "time"
+)
+
+func main() {
+ mypid := os.Getpid()
+ fmt.Printf("pidA = %v\n", mypid);
+
+ fmt.Printf("[%v]: Run %v\n", mypid, os.Args[1:])
+
+ cmd := exec.Command(os.Args[1], os.Args[2:]...)
+ cmd.SysProcAttr = &syscall.SysProcAttr {
+ Ptrace: true,
+ }
+ cmd.Stderr = os.Stderr
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+
+ cmd.Start()
+ child := cmd.Process
+ fmt.Printf("[%d]: pidB = %v\n", mypid, child.Pid)
+
+ sigZero := syscall.Signal(0)
+ if err := child.Signal(sigZero); err != nil {
+ fmt.Printf("[%d]: Error: signal right after start (go): %v\n", mypid, err)
+ } else {
+ fmt.Printf("[%d]: Successfully sent a signal right after start (go)\n", mypid)
+ }
+
+ if err := cmd.Wait(); err != nil {
+ fmt.Printf("[%v]: Wait returned: %v\n", mypid, err)
+ }
+
+ for {
+ time.Sleep(time.Second * 2)
+
+ var regs syscall.PtraceRegs
+ fmt.Printf("[%v]: Get child registers, when stopped\n", mypid)
+ if err := syscall.PtraceGetRegs(child.Pid, ®s); err != nil {
+ panic(err)
+ }
+ fmt.Printf("[%v]: Orig_rax = %v\n", mypid, regs.Orig_rax)
+
+ if err := child.Signal(syscall.Signal(0)); err != nil {
+ fmt.Printf("Error: signal when stopped (go): %v\n", err)
+ if err := syscall.Kill(child.Pid, sigZero); err != nil {
+ fmt.Printf("Error: signal when stopped (syscall): %v\n", err)
+ } else {
+ fmt.Printf("[%d]: Successfully sent a signal when stopped (syscall)\n", mypid)
+ }
+ } else {
+ fmt.Printf("[%d]: Successfully sent a signal when stopped (go)\n", mypid)
+ }
+
+ time.Sleep(time.Second)
+ //if err := syscall.PtraceCont(child.Pid, 0); err != nil {
+ if err := syscall.PtraceSyscall(child.Pid, 0); err != nil {
+ // Here the go program will frequently fail with 'no such
+ // process', though the child process is still alive (which is
+ // checked by sending sigZero below). This is quite reliable
+ // reproducible with sleep for at least 1 second (above). Without
+ // sleep all work fine. The reason is unknown.
+ if err := syscall.Kill(child.Pid, sigZero); err != nil {
+ fmt.Printf("Error: signal at failed ptrace_restart (syscall): %v\n", err)
+ } else {
+ fmt.Printf("[%d]: Successfully sent a signal at failed ptrace_restart (syscall)\n", mypid)
+ }
+ panic(err)
+ }
+
+ //time.Sleep(time.Second)
+ fmt.Printf("[%v]: Get child registers, when running\n", mypid)
+ // Following register quire will fail in any case, because child is
+ // not stopped!
+ if err := syscall.PtraceGetRegs(child.Pid, ®s); err != nil {
+ fmt.Printf("[%d]: Error: PTRACE_GETREGS: %v\n", mypid, err)
+ }
+ fmt.Printf("[%v]: Orig_rax = %v\n", mypid, regs.Orig_rax)
+
+ if err := child.Signal(syscall.Signal(0)); err != nil {
+ fmt.Printf("Error: signal when running (go): %v\n", err)
+ if err := syscall.Kill(child.Pid, sigZero); err != nil {
+ fmt.Printf("Error: signal when running (syscall): %v\n", err)
+ } else {
+ fmt.Printf("[%d]: Successfully sent a signal when running (syscall)\n", mypid)
+ }
+ } else {
+ fmt.Printf("[%d]: Successfully sent a signal when running (go)\n", mypid)
+ }
+
+
+ var ws syscall.WaitStatus
+ if _, err := syscall.Wait4(child.Pid, &ws, 0, nil); err != nil {
+ panic(err)
+ }
+
+ if ws.Exited() {
+ fmt.Printf("[%v]: Child exited with status %v\n", mypid, ws.ExitStatus())
+ break
+
+ } else if ws.Signaled() {
+ fmt.Printf("[%v]: Child killed by signal %v\n", mypid, ws.Signal().String())
+ break
+
+ } else if ws.Stopped() {
+ fmt.Printf("[%v]: Child is traced and and is about to receive %v\n", mypid, ws.StopSignal().String())
+
+ } else if ws.Continued() {
+ fmt.Printf("[%v]: Child continued\n", mypid)
+
+ } else {
+ fmt.Printf("[%d]: Child is elsewhere\n", mypid);
+ }
+ }
+}