func handleSigchld(mpid int) *resultPack { for { var status syscall.WaitStatus var spid int var err error spid, err = syscall.Wait4(-mpid, &status, syscall.WNOHANG|syscall.WALL, nil) if err != nil { poePanic(err, "wait4 failed") } else if spid == 0 { return nil } if spid == mpid && status.Exited() { return &resultPack{POE_SUCCESS, status.ExitStatus(), ""} } else if spid == mpid && status.Signaled() { return &resultPack{POE_SIGNALED, -1, fmt.Sprintf("Program terminated with signal %d (%s)", int(status.Signal()), status.Signal().String())} } else if status.Stopped() { e := status >> 16 & 0xff switch e { case PTRACE_EVENT_SECCOMP: if res := handleSyscall(spid); res != nil { return res } case syscall.PTRACE_EVENT_CLONE, syscall.PTRACE_EVENT_FORK, syscall.PTRACE_EVENT_VFORK: syscall.PtraceCont(spid, 0) default: syscall.PtraceCont(spid, int(status.StopSignal())) } } } }
func handleSyscall(spid int) *resultPack { var regs syscall.PtraceRegs if err := syscall.PtraceGetRegs(spid, ®s); err != nil { poePanic(err, "getreg failed") } switch regs.Orig_rax { case syscall.SYS_KILL: goto allow default: goto kill } kill: if err := syscall.Kill(spid, syscall.SIGKILL); err != nil { poePanic(err, "failed to kill process which called prohibited syscall") } else if name, err := ResolveSeccompSyscallName(regs.Orig_rax); err != nil { poePanic(err, "seccomp_syscall_resolve_num_arch() failed") } else { return &resultPack{POE_SIGNALED, -1, fmt.Sprintf("System call %s is blocked", name)} } allow: if err := syscall.PtraceCont(spid, 0); err != nil { poePanic(err, "PTRACE_CONT failed") } return nil }
func (p *Process) Continue() error { err := syscall.PtraceCont(p.Pid, 0) if err == nil { p.isRunning = true } return err }
func Tracer() { var train = false var cmd string var cmdArgs []string var p *oz.Profile var noprofile = flag.Bool("train", false, "Training mode") var debug = flag.Bool("debug", false, "Debug") var appendpolicy = flag.Bool("append", false, "Append to existing policy if exists") var trainingoutput = flag.String("output", "", "Training policy output file") flag.Parse() var args = flag.Args() if *noprofile == true { train = true // TODO: remove hardcoded path and read prefix from /etc/oz.conf cmd = "/usr/bin/oz-seccomp" cmdArgs = append([]string{"-mode=train"}, args...) } else { p = new(oz.Profile) if err := json.NewDecoder(os.Stdin).Decode(&p); err != nil { log.Error("unable to decode profile data: %v", err) os.Exit(1) } if p.Seccomp.Mode == oz.PROFILE_SECCOMP_TRAIN { train = true } *debug = p.Seccomp.Debug cmd = args[0] cmdArgs = args[1:] } var cpid = 0 done := false log.Info("Tracer running command (%v) arguments (%v)\n", cmd, cmdArgs) c := exec.Command(cmd) c.SysProcAttr = &syscall.SysProcAttr{Ptrace: true} c.Env = os.Environ() c.Args = append(c.Args, cmdArgs...) if *noprofile == false { pi, err := c.StdinPipe() if err != nil { fmt.Errorf("error creating stdin pipe for tracer process: %v", err) os.Exit(1) } jdata, err := json.Marshal(p) if err != nil { fmt.Errorf("Unable to marshal seccomp state: %+v", err) os.Exit(1) } io.Copy(pi, bytes.NewBuffer(jdata)) pi.Close() } children := make(map[int]bool) renderFunctions := getRenderingFunctions() trainingset := make(map[int]bool) trainingargs := make(map[int]map[int][]uint) if err := c.Start(); err == nil { cpid = c.Process.Pid children[c.Process.Pid] = true var s syscall.WaitStatus pid, err := syscall.Wait4(-1, &s, syscall.WALL, nil) children[pid] = true if err != nil { log.Error("Error (wait4) err:%v pid:%i", err, pid) } log.Info("Tracing child pid: %v\n", pid) for done == false { pflags := unix.PTRACE_O_TRACESECCOMP pflags |= unix.PTRACE_O_TRACEFORK pflags |= unix.PTRACE_O_TRACEVFORK pflags |= unix.PTRACE_O_TRACECLONE pflags |= C.PTRACE_O_EXITKILL syscall.PtraceSetOptions(pid, pflags) syscall.PtraceCont(pid, 0) pid, err = syscall.Wait4(-1, &s, syscall.WALL, nil) if err != nil { log.Error("Error (wait4) err:%v pid:%i children:%v\n", err, pid, children) done = true continue } children[pid] = true if s.Exited() == true { delete(children, pid) log.Info("Child pid %v finished.\n", pid) if len(children) == 0 { done = true } continue } if s.Signaled() == true { log.Error("Pid signaled, pid: %v signal: %v", pid, s) delete(children, pid) continue } switch uint32(s) >> 8 { case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_SECCOMP << 8): var regs syscall.PtraceRegs err = syscall.PtraceGetRegs(pid, ®s) if err != nil { log.Error("Error (ptrace): %v", err) } systemcall, err := syscallByNum(getSyscallNumber(regs)) if err != nil { log.Error("Error: %v", err) continue } /* Render the system call invocation */ r := getSyscallRegisterArgs(regs) call := "" if train == true { trainingset[getSyscallNumber(regs)] = true if systemcall.captureArgs != nil { for c, i := range systemcall.captureArgs { if i == 1 { if trainingargs[getSyscallNumber(regs)] == nil { trainingargs[getSyscallNumber(regs)] = make(map[int][]uint) } if contains(trainingargs[getSyscallNumber(regs)][c], uint(r[c])) == false { trainingargs[getSyscallNumber(regs)][c] = append(trainingargs[getSyscallNumber(regs)][c], uint(r[c])) } } } } } if f, ok := renderFunctions[getSyscallNumber(regs)]; ok { call, err = f(pid, r) if err != nil { log.Info("%v", err) continue } if *debug == true { call += "\n " + renderSyscallBasic(pid, systemcall, regs) } } else { call = renderSyscallBasic(pid, systemcall, regs) } log.Info("seccomp hit on sandbox pid %v (%v) syscall %v (%v):\n %s", pid, getProcessCmdLine(pid), systemcall.name, systemcall.num, call) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_EXIT << 8): if *debug == true { log.Error("Ptrace exit event detected pid %v (%s)", pid, getProcessCmdLine(pid)) } delete(children, pid) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_CLONE << 8): newpid, err := syscall.PtraceGetEventMsg(pid) if err != nil { log.Error("PTrace event message retrieval failed: %v", err) } children[int(newpid)] = true if *debug == true { log.Error("Ptrace clone event detected pid %v (%s)", pid, getProcessCmdLine(pid)) } continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_FORK << 8): if *debug == true { log.Error("PTrace fork event detected pid %v (%s)", pid, getProcessCmdLine(pid)) } newpid, err := syscall.PtraceGetEventMsg(pid) if err != nil { log.Error("PTrace event message retrieval failed: %v", err) } children[int(newpid)] = true continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_VFORK << 8): if *debug == true { log.Error("Ptrace vfork event detected pid %v (%s)", pid, getProcessCmdLine(pid)) } newpid, err := syscall.PtraceGetEventMsg(pid) if err != nil { log.Error("PTrace event message retrieval failed: %v", err) } children[int(newpid)] = true continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_VFORK_DONE << 8): if *debug == true { log.Error("Ptrace vfork done event detected pid %v (%s)", pid, getProcessCmdLine(pid)) } newpid, err := syscall.PtraceGetEventMsg(pid) if err != nil { log.Error("PTrace event message retrieval failed: %v", err) } children[int(newpid)] = true continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_EXEC << 8): if *debug == true { log.Error("Ptrace exec event detected pid %v (%s)", pid, getProcessCmdLine(pid)) } continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_STOP << 8): if *debug == true { log.Error("Ptrace stop event detected pid %v (%s)", pid, getProcessCmdLine(pid)) } continue case uint32(unix.SIGTRAP): if *debug == true { log.Error("SIGTRAP detected in pid %v (%s)", pid, getProcessCmdLine(pid)) } continue case uint32(unix.SIGCHLD): if *debug == true { log.Error("SIGCHLD detected pid %v (%s)", pid, getProcessCmdLine(pid)) } continue case uint32(unix.SIGSTOP): if *debug == true { log.Error("SIGSTOP detected pid %v (%s)", pid, getProcessCmdLine(pid)) } continue case uint32(unix.SIGSEGV): if *debug == true { log.Error("SIGSEGV detected pid %v (%s)", pid, getProcessCmdLine(pid)) } err = syscall.Kill(pid, 9) if err != nil { log.Error("kill: %v", err) os.Exit(1) } delete(children, pid) continue default: y := s.StopSignal() if *debug == true { log.Error("Child stopped for unknown reasons pid %v status %v signal %i (%s)", pid, s, y, getProcessCmdLine(pid)) } continue } } if train == true { var u *user.User var e error u, e = user.Current() var resolvedpath = "" if e != nil { log.Error("user.Current(): %v", e) } if *trainingoutput != "" { resolvedpath = *trainingoutput } else { if *noprofile == false { resolvedpath, e = fs.ResolvePathNoGlob(p.Seccomp.TrainOutput, u) if e != nil { log.Error("resolveVars(): %v", e) } } else { s := fmt.Sprintf("${HOME}/%s-%d.seccomp", fname(os.Args[2]), cpid) resolvedpath, e = fs.ResolvePathNoGlob(s, u) } } policyout := "execve:1\n" for call := range trainingset { done := false for c := range trainingargs { if c == call { for a, v := range trainingargs[c] { sc, _ := syscallByNum(call) policyout += fmt.Sprintf("%s:%s\n", sc.name, genArgs(uint(a), (v))) done = true } } } if done == false { sc, _ := syscallByNum(call) policyout += fmt.Sprintf("%s:1\n", sc.name) } } if *appendpolicy == true { log.Error("Not yet implemented.") } f, err := os.OpenFile(resolvedpath, os.O_CREATE|os.O_RDWR, 0600) if err == nil { _, err := f.WriteString(policyout) if err != nil { log.Error("Error writing policy file: %v", err) } err = f.Close() if err != nil { log.Error("Error closing policy file: %v", err) } } else { log.Error("Error opening policy file \"%s\": %v", resolvedpath, err) } } } }
func (t *thread) ptraceContWithSignal(sig int) os.Error { err := syscall.PtraceCont(t.tid, sig) return os.NewSyscallError("ptrace(CONT)", err) }
func (t *thread) ptraceCont() os.Error { err := syscall.PtraceCont(t.tid, 0) return os.NewSyscallError("ptrace(CONT)", err) }
func run() { // If the debugger itself is multi-threaded, ptrace calls must come from // the same thread that originally attached to the remote thread. runtime.LockOSThread() f, err := os.Open(exeFilename) if err != nil { log.Printf(`%q not found. Did you run "go build ." in that directory?`, exeFilename) log.Fatalf("Open: %v", err) } defer f.Close() dwarfData, err := loadDwarfData(f) if err != nil { log.Fatalf("loadDwarfData: %v", err) } proc, err := os.StartProcess(exeFilename, []string{exeFilename}, &os.ProcAttr{ Files: []*os.File{ os.Stdin, os.Stdout, os.Stderr, }, Sys: &syscall.SysProcAttr{ Ptrace: true, Pdeathsig: syscall.SIGKILL, }, }) if err != nil { log.Fatalf("StartProcess: %v", err) } fmt.Printf("\tproc.Pid=%d\n", proc.Pid) _, status, err := wait(proc.Pid) if err != nil { log.Fatalf("wait: %v", err) } if status != 0x00057f { // 0x05=SIGTRAP, 0x7f=stopped. log.Fatalf("status: got %#x, want %#x", status, 0x57f) } err = syscall.PtraceSetOptions(proc.Pid, syscall.PTRACE_O_TRACECLONE|syscall.PTRACE_O_TRACEEXIT) if err != nil { log.Fatalf("PtraceSetOptions: %v", err) } addr, err := lookupSym(dwarfData, "fmt.Printf") if err != nil { log.Fatalf("lookupSym: %v", err) } fmt.Printf("\tfmt.Printf=%#x\n", addr) var buf [1]byte if err := peek(proc.Pid, addr, buf[:1]); err != nil { log.Fatalf("peek: %v", err) } breakpoints := map[uint64]breakpoint{ addr: {pc: addr, origInstr: buf[0]}, } buf[0] = breakpointInstr if err := poke(proc.Pid, addr, buf[:1]); err != nil { log.Fatalf("poke: %v", err) } err = syscall.PtraceCont(proc.Pid, 0) if err != nil { log.Fatalf("PtraceCont: %v", err) } for { pid, status, err := wait(-1) if err != nil { log.Fatalf("wait: %v", err) } switch status { case 0x00057f: // 0x05=SIGTRAP, 0x7f=stopped. regs := syscall.PtraceRegs{} if err := syscall.PtraceGetRegs(pid, ®s); err != nil { log.Fatalf("PtraceGetRegs: %v", err) } regs.Rip -= breakpointInstrLen if err := syscall.PtraceSetRegs(pid, ®s); err != nil { log.Fatalf("PtraceSetRegs: %v", err) } bp, ok := breakpoints[regs.Rip] if !ok { log.Fatalf("no breakpoint for address %#x\n", regs.Rip) } buf[0] = bp.origInstr if err := poke(pid, addr, buf[:1]); err != nil { log.Fatalf("poke: %v", err) } fmt.Printf("\thit breakpoint at %#x, pid=%5d\n", regs.Rip, pid) if err := syscall.PtraceSingleStep(pid); err != nil { log.Fatalf("PtraceSingleStep: %v", err) } _, status, err := wait(pid) if err != nil { log.Fatalf("wait: %v", err) } if status != 0x00057f { log.Fatalf("PtraceSingleStep: unexpected status %#x\n", status) } buf[0] = breakpointInstr if err := poke(pid, addr, buf[:1]); err != nil { log.Fatalf("poke: %v", err) } case 0x00137f: // 0x13=SIGSTOP, 0x7f=stopped. // No-op. case 0x03057f: // 0x05=SIGTRAP, 0x7f=stopped, 0x03=PTRACE_EVENT_CLONE. msg, err := syscall.PtraceGetEventMsg(pid) if err != nil { log.Fatalf("PtraceGetEventMsg: %v", err) } fmt.Printf("\tclone: new pid=%d\n", msg) default: log.Fatalf("unexpected status %#x\n", status) } err = syscall.PtraceCont(pid, 0) if err != nil { log.Fatalf("PtraceCont: %v", err) } } }
func Tracer() { p := new(oz.Profile) if err := json.NewDecoder(os.Stdin).Decode(&p); err != nil { log.Error("unable to decode profile data: %v", err) os.Exit(1) } var proc_attr syscall.ProcAttr var sys_attr syscall.SysProcAttr sys_attr.Ptrace = true done := false proc_attr.Sys = &sys_attr cmd := os.Args[1] cmdArgs := os.Args[2:] log.Info("Tracer running command (%v) arguments (%v)\n", cmd, cmdArgs) c := exec.Command(cmd) c.SysProcAttr = &syscall.SysProcAttr{Ptrace: true} c.Env = os.Environ() c.Args = append(c.Args, cmdArgs...) pi, err := c.StdinPipe() if err != nil { fmt.Errorf("error creating stdin pipe for tracer process: %v", err) os.Exit(1) } jdata, err := json.Marshal(p) if err != nil { fmt.Errorf("Unable to marshal seccomp state: %+v", err) os.Exit(1) } io.Copy(pi, bytes.NewBuffer(jdata)) log.Info(string(jdata)) pi.Close() children := make(map[int]bool) if err := c.Start(); err == nil { children[c.Process.Pid] = true var s syscall.WaitStatus pid, err := syscall.Wait4(-1, &s, syscall.WALL, nil) children[pid] = true if err != nil { log.Error("Error (wait4): %v", err) } log.Info("Tracing child pid: %v\n", pid) for done == false { syscall.PtraceSetOptions(pid, unix.PTRACE_O_TRACESECCOMP|unix.PTRACE_O_TRACEFORK|unix.PTRACE_O_TRACEVFORK|unix.PTRACE_O_TRACECLONE|unix.PTRACE_O_TRACEEXIT) syscall.PtraceCont(pid, 0) pid, err = syscall.Wait4(-1, &s, syscall.WALL, nil) if err != nil { log.Error("Error (wait4): %v\n", err) if len(children) == 0 { done = true } continue } children[pid] = true if s.Exited() == true { delete(children, pid) log.Info("Child pid %v finished.\n", pid) if len(children) == 0 { done = true } continue } if uint32(s)>>8 == (uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_SECCOMP << 8)) { if err != nil { log.Error("Error (ptrace): %v", err) continue } var regs syscall.PtraceRegs err = syscall.PtraceGetRegs(pid, ®s) if err != nil { log.Error("Error (ptrace): %v", err) } systemcall, err := syscallByNum(int(regs.Orig_rax)) if err != nil { log.Error("Error: %v", err) continue } var callrep string = fmt.Sprintf("%s(", systemcall.name) var reg uint64 = 0 for arg := range systemcall.args { if systemcall.args[arg] == 0 { break } if arg > 0 { callrep += fmt.Sprintf(",") } switch arg { case 0: reg = regs.Rdi case 1: reg = regs.Rsi case 2: reg = regs.Rdx case 3: reg = regs.Rcx case 4: reg = regs.R8 case 5: reg = regs.R9 } if systemcall.args[arg] == STRINGARG { str, err := readStringArg(pid, uintptr(reg)) if err != nil { log.Error("Error: %v", err) } else { callrep += fmt.Sprintf("\"%s\"", str) } } else if systemcall.args[arg] == INTARG { callrep += fmt.Sprintf("%d", uint64(reg)) } else { /* Stringify pointers in writes to stdout/stderr */ write, err := syscallByName("write") if err != nil { log.Error("Error: %v", err) } if systemcall.num == write.num && (regs.Rdi == uint64(syscall.Stdout) || regs.Rdi == uint64(syscall.Stderr)) { str, err := readStringArg(pid, uintptr(reg)) if err != nil { log.Error("Error %v", err) } else { if isPrintableASCII(str) == true { callrep += fmt.Sprintf("\"%s\"", str) } else { callrep += fmt.Sprintf("0x%X", uintptr(reg)) } } } else { callrep += fmt.Sprintf("0x%X", uintptr(reg)) } } } callrep += ")" log.Info("==============================================\nseccomp hit on sandbox pid %v (%v) syscall %v (%v): \n\n%s\nI ==============================================\n\n", pid, getProcessCmdLine(pid), systemcall.name, regs.Orig_rax, callrep) } } } else { log.Error("Error: %v", err) } }
func Tracer() { p := new(oz.Profile) if err := json.NewDecoder(os.Stdin).Decode(&p); err != nil { log.Error("unable to decode profile data: %v", err) os.Exit(1) } var proc_attr syscall.ProcAttr var sys_attr syscall.SysProcAttr sys_attr.Ptrace = true done := false proc_attr.Sys = &sys_attr cmd := os.Args[1] cmdArgs := os.Args[2:] log.Info("Tracer running command (%v) arguments (%v)\n", cmd, cmdArgs) c := exec.Command(cmd) c.SysProcAttr = &syscall.SysProcAttr{Ptrace: true} c.Env = os.Environ() c.Args = append(c.Args, cmdArgs...) pi, err := c.StdinPipe() if err != nil { fmt.Errorf("error creating stdin pipe for tracer process: %v", err) os.Exit(1) } jdata, err := json.Marshal(p) if err != nil { fmt.Errorf("Unable to marshal seccomp state: %+v", err) os.Exit(1) } io.Copy(pi, bytes.NewBuffer(jdata)) log.Info(string(jdata)) pi.Close() children := make(map[int]bool) renderFunctions := getRenderingFunctions() if err := c.Start(); err == nil { children[c.Process.Pid] = true var s syscall.WaitStatus pid, err := syscall.Wait4(-1, &s, syscall.WALL, nil) children[pid] = true if err != nil { log.Error("Error (wait4) here first: %v %i", err, pid) } log.Info("Tracing child pid: %v\n", pid) for done == false { syscall.PtraceSetOptions(pid, unix.PTRACE_O_TRACESECCOMP|unix.PTRACE_O_TRACEFORK|unix.PTRACE_O_TRACEVFORK|unix.PTRACE_O_TRACECLONE) syscall.PtraceCont(pid, 0) pid, err = syscall.Wait4(-1, &s, syscall.WALL, nil) if err != nil { log.Error("Error (wait4) here: %v %i %v\n", err, pid, children) if len(children) == 0 { done = true } continue } children[pid] = true if s.Exited() == true { delete(children, pid) log.Info("Child pid %v finished.\n", pid) if len(children) == 0 { done = true } continue } if s.Signaled() == true { log.Error("Other pid signalled %v %v", pid, s) delete(children, pid) continue } switch uint32(s) >> 8 { case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_SECCOMP << 8): if err != nil { log.Error("Error (ptrace): %v", err) continue } var regs syscall.PtraceRegs err = syscall.PtraceGetRegs(pid, ®s) if err != nil { log.Error("Error (ptrace): %v", err) } systemcall, err := syscallByNum(getSyscallNumber(regs)) if err != nil { log.Error("Error: %v", err) continue } /* Render the system call invocation */ r := getSyscallRegisterArgs(regs) call := "" if f, ok := renderFunctions[getSyscallNumber(regs)]; ok { call, err = f(pid, r) if err != nil { log.Info("%v", err) continue } } else { call = renderSyscallBasic(pid, systemcall, regs) } log.Info("==============================================\nseccomp hit on sandbox pid %v (%v) syscall %v (%v):\n\n%s\nI ==============================================\n\n", pid, getProcessCmdLine(pid), systemcall.name, systemcall.num, call) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_EXIT << 8): log.Error("Ptrace exit event detected pid %v (%s)", pid, getProcessCmdLine(pid)) case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_CLONE << 8): log.Error("Ptrace clone event detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_FORK << 8): log.Error("PTrace fork event detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_VFORK << 8): log.Error("Ptrace vfork event detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_VFORK_DONE << 8): log.Error("Ptrace vfork done event detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_EXEC << 8): log.Error("Ptrace exec event detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGTRAP) | (unix.PTRACE_EVENT_STOP << 8): log.Error("Ptrace stop event detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGTRAP): log.Error("SIGTRAP detected in pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGCHLD): log.Error("SIGCHLD detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue case uint32(unix.SIGSTOP): log.Error("SIGSTOP detected pid %v (%s)", pid, getProcessCmdLine(pid)) continue default: y := s.StopSignal() log.Error("Child stopped for unknown reasons pid %v status %v signal %i (%s)", pid, s, y, getProcessCmdLine(pid)) continue } } } }
func (t *Tracer) Cont(sig syscall.Signal) error { return syscall.PtraceCont(t.Process.Pid, int(sig)) }
func (s *Server) ptraceCont(pid int, signal int) (err error) { s.fc <- func() error { return syscall.PtraceCont(pid, signal) } return <-s.ec }