Beispiel #1
0
// waitStatusToStateUpdate converts syscall.WaitStatus to a StateUpdate.
func waitStatusToStateUpdate(ws syscall.WaitStatus) *stateUpdate {
	switch {
	case ws.Exited():
		es := ws.ExitStatus()
		if es == 0 {
			return newExitedStateUpdate(ok)
		}
		return newExitedStateUpdate(newFailure(fmt.Sprint(es)))
	case ws.Signaled():
		msg := fmt.Sprintf("signaled %v", ws.Signal())
		if ws.CoreDump() {
			msg += " (core dumped)"
		}
		return newUnexitedStateUpdate(msg)
	case ws.Stopped():
		msg := fmt.Sprintf("stopped %v", ws.StopSignal())
		trap := ws.TrapCause()
		if trap != -1 {
			msg += fmt.Sprintf(" (trapped %v)", trap)
		}
		return newUnexitedStateUpdate(msg)
	case ws.Continued():
		return newUnexitedStateUpdate("continued")
	default:
		return newUnexitedStateUpdate(fmt.Sprint("unknown status", ws))
	}
}
Beispiel #2
0
func (c *CloneParams) CloneFrozen() (int, error) {
	pid := callClone(c)
	// TODO: clone errors?
	c.CommWriter.Close()
	c.stdhandles.Close()
	c.comm = make(chan CommStatus)
	go commReader(c.CommReader, c.comm)

	var status syscall.WaitStatus
	for {
		wpid, err := syscall.Wait4(pid, &status, 0, nil) // TODO: rusage
		if err != nil {
			return -1, os.NewSyscallError("Wait4", err)
		}
		if wpid == pid {
			break
		}
	}
	if status.Stopped() && status.StopSignal() == syscall.SIGTRAP {
		return pid, nil
	}
	if status.Exited() {
		co, ok := <-c.comm
		if ok {
			return -1, childError(co)
		}
		return -1, fmt.Errorf("DAFUQ")
	}
	err := syscall.Kill(pid, syscall.SIGKILL)
	if err != nil {
		return -1, os.NewSyscallError("Kill", err)
	}
	return -1, fmt.Errorf("traps, signals, dafuq is this")
}
Beispiel #3
0
// waitStatusToError converts syscall.WaitStatus to an Error.
func waitStatusToError(ws syscall.WaitStatus) error {
	switch {
	case ws.Exited():
		es := ws.ExitStatus()
		if es == 0 {
			return nil
		}
		return errors.New(fmt.Sprint(es))
	case ws.Signaled():
		msg := fmt.Sprintf("signaled %v", ws.Signal())
		if ws.CoreDump() {
			msg += " (core dumped)"
		}
		return errors.New(msg)
	case ws.Stopped():
		msg := fmt.Sprintf("stopped %v", ws.StopSignal())
		trap := ws.TrapCause()
		if trap != -1 {
			msg += fmt.Sprintf(" (trapped %v)", trap)
		}
		return errors.New(msg)
	/*
		case ws.Continued():
			return newUnexitedStateUpdate("continued")
	*/
	default:
		return fmt.Errorf("unknown WaitStatus", ws)
	}
}
Beispiel #4
0
func (t *tracerImpl) Run() (err error) {

	if t.cmd.SysProcAttr == nil {
		t.cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
	} else {
		t.cmd.SysProcAttr.Ptrace = true
	}

	runtime.LockOSThread()

	if err = t.cmd.Start(); err != nil {
		return
	}

	var waitStatus syscall.WaitStatus

	if _, err = syscall.Wait4(t.cmd.Process.Pid, &waitStatus, 0, nil); err != nil {
		return
	}

	if waitStatus.Exited() {
		return
	}

	// Set options to detect our syscalls
	if err = syscall.PtraceSetOptions(t.cmd.Process.Pid, syscall.PTRACE_O_TRACESYSGOOD); err != nil {
		return
	}

	var regsEntry, regsExit syscall.PtraceRegs
	// Get first syscall
	if err = syscall.PtraceGetRegs(t.cmd.Process.Pid, &regsEntry); err != nil {
		return
	}

	var exited bool
	for {
		if exited, err = wait_for_syscall(t.cmd.Process.Pid); exited || err != nil {
			return
		}

		// Get syscall info
		if err = syscall.PtraceGetRegs(t.cmd.Process.Pid, &regsEntry); err != nil {
			return
		}

		// Enter syscall
		t.callback(regsEntry, false)

		if exited, err = wait_for_syscall(t.cmd.Process.Pid); exited || err != nil {
			return
		}

		// Get syscall returned value
		if err = syscall.PtraceGetRegs(t.cmd.Process.Pid, &regsExit); err != nil {
			return
		}
		t.callback(regsExit, true)
	}
}
Beispiel #5
0
func (m *Module) IsRunning() bool {
	pid := 0
	if m.Name == "internal:shared" {
		pid = m.SyncProcess.Pid
	} else {
		pid = m.MainProcess.Pid
	}

	var waitstatus syscall.WaitStatus
	wpid, err := syscall.Wait4(pid, &waitstatus, syscall.WNOHANG|syscall.WUNTRACED, nil)
	if err != nil {
		// When would this happen?
		log.Println("Unable to get process wait status:", err)
		// Assume it is not running
		return false
	}

	// If status is not available, the pid is 0.
	if wpid == 0 {
		return true
	}
	if waitstatus.Exited() {
		delete(modules, m.Name)
		return false
	}
	return true
}
Beispiel #6
0
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()))
			}
		}
	}
}
Beispiel #7
0
func printStatus(ws syscall.WaitStatus) string {
	switch {
	case ws.Exited():
		es := ws.ExitStatus()
		if es == 0 {
			return ""
		}
		return fmt.Sprintf("exited %v", es)
	case ws.Signaled():
		msg := fmt.Sprintf("signaled %v", ws.Signal())
		if ws.CoreDump() {
			msg += " (core dumped)"
		}
		return msg
	case ws.Stopped():
		msg := fmt.Sprintf("stopped %v", ws.StopSignal())
		trap := ws.TrapCause()
		if trap != -1 {
			msg += fmt.Sprintf(" (trapped %v)", trap)
		}
		return msg
	case ws.Continued():
		return "continued"
	default:
		return fmt.Sprintf("unknown status %v", ws)
	}
}
Beispiel #8
0
// StartProcess kicks off the event loop and forever waits for signals from
// the traced process. This is currently done in a super-silly fashion and will
// hopefully benefit from Go channels/goroutines in the future.
func (p *Process) StartProcess() (ret int) {
	var status syscall.WaitStatus

L:
	for {
		_, err := syscall.Wait4( /*p.Pid*/ -1, &status, 0, nil)
		p.isRunning = false

		switch {
		// status == 0  means terminated??
		case status.Exited() || status == 0 || err != nil:
			ret = status.ExitStatus()
			break L
		case status.Stopped():
			if bp, hit := p.InBreakpoint(); hit {
				p.handleBreakpoint(bp)
			}

		//case status.Continued():
		//case status.CoreDump():
		//case status.Signaled():
		//case status.ExitStatus():
		//case status.StopSignal():
		//case status.TrapCause():
		default:
			// fmt.Printf("Got status: %v\n", status)
		}

		p.Continue()

	}
	return
}
Beispiel #9
0
func ChildWaitingFunc(pid int, sig chan *ChildWaitData) {
	var status syscall.WaitStatus
	var rusage syscall.Rusage
	result := &ChildWaitData{}
	for {
		wpid, err := syscall.Wait4(pid, &status, syscall.WUNTRACED|syscall.WCONTINUED, &rusage)
		if wpid != pid {
			continue
		}

		if status.Exited() {
			result.ExitCode = uint32(status.ExitStatus())
			break
		}
		if status.Stopped() {
			result.SuccessCode |= EF_STOPPED
			result.StopSignal = uint32(status.StopSignal())
			syscall.Kill(pid, syscall.SIGKILL)
		}
		if status.Signaled() {
			result.SuccessCode |= EF_KILLED_BY_OTHER
			result.KillSignal = uint32(status.Signal())
			break
		}
		if err != nil {
			break
		}
	}
	result.RusageCpuUser = time.Nanosecond * time.Duration(rusage.Utime.Nano())
	result.RusageCpuKernel = time.Nanosecond * time.Duration(rusage.Stime.Nano())
	sig <- result
	close(sig)
}
Beispiel #10
0
// Called on running checks, to determine if they have finished
// running.
//
// If the Check has not finished executing, returns false.
//
// If the Check has been running for longer than its Timeout,
// a SIGTERM (and failing that a SIGKILL) is issued to forcibly
// terminate the rogue Check process. In either case, this returns
// as if the check has not yet finished, and Reap() will need to be
// called again to fully reap the Check
//
// If the Check has finished execution (on its own, or via forced
// termination), it will return true.
//
// Once complete, some additional meta-stats for the check execution
// are appended to the check output, to be submit up to bolo
func (self *Check) Reap() bool {
	pid := self.process.Process.Pid

	var ws syscall.WaitStatus
	status, err := syscall.Wait4(pid, &ws, syscall.WNOHANG, nil)
	if err != nil {
		log.Error("Error waiting on check %s[%d]: %s", self.Name, pid, err.Error())
		return false
	}
	if status == 0 {
		// self to see if we need to sigkill due to failed sigterm
		if time.Now().After(self.started_at.Add(time.Duration(self.Timeout+2) * time.Second)) {
			log.Warn("Check %s[%d] has been running too long, sending SIGKILL", self.Name, pid)
			if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
				log.Error("Error sending SIGKILL to check %s[%d]: %s", self.Name, pid, err.Error())
			}
			self.sig_kill = true
		}
		// self to see if we need to sigterm due to self timeout expiry
		if !self.sig_kill && time.Now().After(self.started_at.Add(time.Duration(self.Timeout)*time.Second)) {
			log.Warn("Check %s[%d] has been running too long, sending SIGTERM", self.Name, pid)
			if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
				log.Error("Error sending SIGTERM to check %s[%d]: %s", self.Name, pid, err.Error())
			}
			self.sig_term = true
		}
		return false
	}

	self.ended_at = time.Now()
	self.running = false
	self.duration = time.Since(self.started_at)
	self.latency = self.started_at.Sub(self.next_run)
	self.output = string(self.stdout.Bytes())
	self.err_msg = string(self.stderr.Bytes())

	if ws.Exited() {
		self.rc = ws.ExitStatus()
	} else {
		log.Debug("Check %s[%d] exited abnormally (signaled/stopped). Setting rc to UNKNOWN", self.Name, pid)
		self.rc = UNKNOWN
	}
	if self.rc > UNKNOWN {
		log.Debug("Check %s[%d] returned with an invalid exit code. Setting rc to UNKOWN", self.Name, pid)
		self.rc = UNKNOWN
	}

	self.reschedule()

	if self.ended_at.After(self.next_run) {
		timeout_triggered := "not reached"
		if self.sig_term || self.sig_kill {
			timeout_triggered = "reached"
		}
		log.Warn("Check %s[%d] took %0.3f seconds to run, at interval %d (timeout of %d was %s)",
			self.Name, pid, self.duration.Seconds(), self.Every, self.Timeout, timeout_triggered)
	}
	return true
}
Beispiel #11
0
func (self *server) Execute(req *pb.ExecutionRequest, resp pb.Builder_ExecuteServer) error {
	if len(req.Args) == 0 {
		return fmt.Errorf("Request has no command to execute.")
	}

	var args []string
	if len(req.Args) > 1 {
		args = req.Args[1:]
	}

	if req.BuildEnv == nil {
		return fmt.Errorf("No build environment present")
	} else if err := sig.VerifyEnv(req.BuildEnv); err != nil {
		return fmt.Errorf("Failure verifying build environment: %s", err)
	}
	cmd := exec.Command(req.Args[0], args...)
	cmd.Env = convertEnv(req.GetEnv())
	cmd.Stdin = bytes.NewReader(req.Stdin)
	cmd.Dir = req.BuildEnv.Path

	glog.V(1).Infof("Commands to execute %v (build dir: %s)", req, cmd.Dir)

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	if err = cmd.Start(); err != nil {
		return err
	}
	streamReader(stdoutPipe, stderrPipe, resp)
	err = cmd.Wait()

	var status syscall.WaitStatus
	if err != nil {
		if _, ok := err.(*exec.ExitError); ok {
			status = err.(*exec.ExitError).Sys().(syscall.WaitStatus)
		} else {
			return err
		}
	} else if cmd.ProcessState != nil {
		status = cmd.ProcessState.Sys().(syscall.WaitStatus)
	}
	s := &pb.Status{
		CoreDump:   status.CoreDump(),
		Exited:     status.Exited(),
		ExitStatus: int32(status.ExitStatus()),
		Signaled:   status.Signaled(),
		Signal:     int32(status.Signal()),
	}
	resp.Send(&pb.ExecutionResponse{Status: s})
	return nil
}
Beispiel #12
0
func NewExternalCmdExit(name string, ws syscall.WaitStatus, pid int) error {
	if ws.Exited() && ws.ExitStatus() == 0 {
		return nil
	}
	if !ws.Stopped() {
		pid = 0
	}
	return ExternalCmdExit{ws, name, pid}
}
Beispiel #13
0
func (t *PTracer) handleStopped(pid int, status syscall.WaitStatus) {
	signal := syscall.Signal(0)
	target, err := t.thread(pid)
	if err != nil {
		log.Printf("thread failed: %v", err)
		return
	}

	if !target.attached {
		target.attached = true

		err = syscall.PtraceSetOptions(pid, ptraceOptions)
		if err != nil {
			log.Printf("SetOptions failed, pid=%d, err=%v", pid, err)
			return
		}

	} else if status.Stopped() && status.StopSignal() == syscall.SIGTRAP|ptraceTracesysgoodBit {
		// pid entered Syscall-enter-stop or syscall-exit-stop
		target.syscallStopped()

	} else if status.Stopped() && status.StopSignal() == syscall.SIGTRAP {
		// pid entered PTRACE_EVENT stop
		switch status.TrapCause() {
		case syscall.PTRACE_EVENT_CLONE:
			err := target.handleClone(pid)
			if err != nil {
				log.Printf("clone failed: %v", err)
				return
			}
		default:
			log.Printf("Unknown PTRACE_EVENT %d for pid %d", status.TrapCause(), pid)
		}
	} else if status.Exited() || status.Signaled() {
		// "tracer can safely assume pid will exit"
		t.threadExited(target)
		return
	} else if status.Stopped() {
		// tracee received a non-trace related signal
		signal = status.StopSignal()

		if signal == syscall.SIGSTOP && target.process.detaching {
			t.detachThread(target)
			return
		}
	} else {
		// unknown stop - shouldn't happen!
		log.Printf("Pid %d random stop with status %x", pid, status)
	}

	// Restart stopped caller in syscall trap mode.
	// log.Printf("Restarting pid %d with signal %d", pid, int(signal))
	err = syscall.PtraceSyscall(pid, int(signal))
	if err != nil {
		log.Printf("PtraceSyscall failed, pid=%d, err=%v", pid, err)
	}
}
Beispiel #14
0
func execFgCmd(cmd []string, sigStateChanged chan string) {
	cmdStr := strings.Join(cmd, " ")

	// TODO: Extract start process into common function.
	argv0, err := exec.LookPath(cmd[0])
	if err != nil {
		if cmd[0] != "" {
			fmt.Printf("Unknown command: %s\n", cmd[0])
		}
		// Don't execute new process with empty return. Will cause panic.
		sigPrompt <- struct{}{}
		return
	}
	var procAttr os.ProcAttr
	procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}

	p, err := os.StartProcess(argv0, cmd, &procAttr)
	if err != nil {
		fmt.Printf("Start process %s, %s failed: %v", err, argv0, cmd)
	}

	for {
		sigChild := make(chan os.Signal)
		defer close(sigChild)
		// SIGCONT not receivable: https://github.com/golang/go/issues/8953
		// This causes some bugs. Eg. CONT signal not captured by handler means subsequent KILL or STOP signals will be ignored by this handler.
		signal.Notify(sigChild, syscall.SIGTSTP, syscall.SIGINT, syscall.SIGCONT, syscall.SIGKILL)
		defer signal.Stop(sigChild)

		var ws syscall.WaitStatus
		// Ignoring error. May return "no child processes" error. Eg. Sending Ctrl-c on `cat` command.
		wpid, _ := syscall.Wait4(p.Pid, &ws, syscall.WUNTRACED, nil)

		if ws.Exited() {
			break
		}
		if ws.Stopped() {
			jobHandler(wpid, runningState, cmdStr)
			jobHandler(wpid, suspendedState, cmdStr)
			// Return prompt when fg has become bg
			sigPrompt <- struct{}{}
		}
		//if ws.Continued() {
		//	state = contState
		//}
		if ws == 9 {
			jobHandler(wpid, killedState, cmdStr)
			break
		}
	}

	p.Wait()
	sigPrompt <- struct{}{}
}
Beispiel #15
0
func execBgCmd(cmd []string, sigStateChanged chan string) {
	cmdStr := strings.Join(cmd, " ")

	argv0, err := exec.LookPath(cmd[0])
	if err != nil {
		if cmd[0] != "" {
			fmt.Printf("Unknown command: %s\n", cmd[0])
		}
		sigPrompt <- struct{}{}
		return
	}
	var procAttr os.ProcAttr
	procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}

	p, err := os.StartProcess(argv0, cmd, &procAttr)
	if err != nil {
		fmt.Printf("Start process %s, %s failed: %v", err, argv0, cmd)
	}
	jobHandler(p.Pid, runningState, cmdStr)
	sigPrompt <- struct{}{}

	//FIXME: Bg processes should not receive keyboard signals sent to fg process.

	for {
		sigChild := make(chan os.Signal)
		defer close(sigChild)
		signal.Notify(sigChild, syscall.SIGCHLD)
		defer signal.Stop(sigChild)

		var ws syscall.WaitStatus
		wpid, _ := syscall.Wait4(p.Pid, &ws, syscall.WUNTRACED, nil)

		if ws.Exited() {
			jobHandler(wpid, doneState, cmdStr)
			break
		}
		if ws.Stopped() {
			jobHandler(wpid, suspendedState, cmdStr)
			sigPrompt <- struct{}{}
		}
		//if ws.Continued() {
		//	state = contState
		//}
		if ws == 9 {
			jobHandler(wpid, killedState, cmdStr)
			break
		}
	}

	p.Wait()
	sigPrompt <- struct{}{}
}
Beispiel #16
0
func waitPid(pid int) {
	for {
		var ws syscall.WaitStatus
		_, err := syscall.Wait4(pid, &ws, 0, nil)

		if err != nil {
			panic(err)
		}

		if ws.Exited() {
			break
		}
	}
}
Beispiel #17
0
func waitSyscall(proc *os.Process) bool {
	for {
		syscall.PtraceSyscall(proc.Pid, 0)

		var status syscall.WaitStatus
		syscall.Wait4(proc.Pid, &status, 0, nil)
		if status.Stopped() {
			return false
		}
		if status.Exited() {
			return true
		}
	}
}
Beispiel #18
0
func waitStateUpdate(pid int, update chan<- *StateUpdate) {
	for {
		var ws syscall.WaitStatus
		_, err := syscall.Wait4(pid, &ws, 0, nil)

		if err != nil {
			if err != syscall.ECHILD {
				update <- &StateUpdate{Msg: err.Error()}
			}
			break
		}
		update <- &StateUpdate{
			Terminated: ws.Exited(), Msg: fmt.Sprintf("%v", ws)}
	}
	close(update)
}
Beispiel #19
0
// waitStateUpdate wait(2)s for pid, and feeds the WaitStatus's into a
// *StateUpdate channel after proper conversion.
func waitStateUpdate(pid int, update chan<- *stateUpdate) {
	for {
		var ws syscall.WaitStatus
		_, err := syscall.Wait4(pid, &ws, 0, nil)

		if err != nil {
			update <- newExitedStateUpdate(newFailure(err.Error()))
			break
		}
		update <- waitStatusToStateUpdate(ws)
		if ws.Exited() {
			break
		}
	}
	close(update)
}
Beispiel #20
0
func (t *Task) Tracer() error {
	runtime.LockOSThread()

	if err := syscall.PtraceAttach(t.pid); err != nil {
		return err
	}
	defer func() {
		// syscall.PtraceCont(t.pid, 0)
		syscall.PtraceDetach(t.pid)
	}()

	regsout := new(syscall.PtraceRegs)
	status := new(syscall.WaitStatus)
	var timer *time.Timer
	refresh := func() { t.RefreshFiles(); timer.Stop(); timer = nil }
	for {
		if _, err := syscall.Wait4(t.pid, status, 0, nil); err != nil {
			log.Println("wait failed", err)
			return err
		}
		if status.Exited() {
			log.Println("exited")
			return nil
		}

		if err := syscall.PtraceGetRegs(t.pid, regsout); err != nil {
			log.Println("getregs failed", err)
			return err
		}

		// linux_amd64
		if regsout.Orig_rax == syscall.SYS_OPEN {
			if timer != nil {
				if timer.Stop() == false {
					log.Println("cannot stop the timer")
				}
			}
			timer = time.AfterFunc(1e9, refresh) // Wait until open()s "settle".
		}

		if err := PtraceSyscall(t.pid); err != nil {
			log.Println("PtraceSyscall failed", err)
			return err
		}
	}
	panic("can't reach")
}
Beispiel #21
0
func (server *Server) wait() {

	server.mutex.Lock()
	if server.waiting {
		server.mutex.Unlock()
		return
	}
	server.waiting = true
	server.mutex.Unlock()

	var wstatus syscall.WaitStatus
	var rusage syscall.Rusage
	var last_run bool
	for {
		pid, err := syscall.Wait4(-1, &wstatus, 0, &rusage)
		if err != nil {
			if err == syscall.ECHILD {
				// Run once more to catch any races.
				server.mutex.Lock()
				if server.waiting {
					server.waiting = false
					server.mutex.Unlock()
					last_run = true
					continue
				} else {
					server.mutex.Unlock()
					return
				}
			} else {
				continue
			}
		}
		if wstatus.Exited() {
			process := server.lookup(pid)
			if process != nil {
				process.setExitcode(wstatus.ExitStatus())
			}
		}
		if last_run {
			break
		}
	}
}
Beispiel #22
0
func getFdFromChild(pid, fd int) {
	var wstatus syscall.WaitStatus
	_, errno := syscall.Wait4(pid, &wstatus, 0, nil)
	if errno != 0 {
		fmt.Printf("getFdFromChild: Wait4: %v: %v\n", errno, syscall.Errstr(errno))
		os.Exit(1)
	}
	if wstatus.ExitStatus() != 0 {
		fmt.Printf("openfile exited(%v) with status %v\n", wstatus.Exited(), wstatus.ExitStatus())
		os.Exit(1)
	}
	var smallbuf [10]byte
	var largebuf [4096]byte
	readAndPrint(fd, smallbuf[:])
	recvfd, err := fdpass.Receive(fd)
	if err != nil {
		fmt.Printf("getFdFromChild: fdpass.Receive: %v\n", err)
		os.Exit(1)
	}
	readAndPrint(recvfd, largebuf[:])
	readAndPrint(fd, smallbuf[:])
}
Beispiel #23
0
func toGuardMode(args []string) {
	_, pName := path.Split(os.Args[0])

	for {
		pid, err := syscall.ForkExec(args[0], args, nil)
		if err != nil {
			log.Fatalf("Error - %s fork failed! [%s]", pName, err.Error())
			os.Exit(1)
		}

		childPid = pid

		var ws syscall.WaitStatus
		_, err = syscall.Wait4(childPid, &ws, 0, nil)
		for err == syscall.EINTR {
			_, err = syscall.Wait4(childPid, &ws, 0, nil)
		}

		// Log
		lf, fErr := os.OpenFile("debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if fErr != nil {
			log.Fatalf("Error - open debug.log failed! [%s]", fErr.Error())
		}
		defer lf.Close()

		l := log.New(lf, "", os.O_APPEND)

		if ws.Exited() {
			l.Printf("[%s] Exited - %s exit status %d", sTime(), pName, ws.ExitStatus())
			os.Exit(1)
		}
		if ws.Signaled() {
			l.Printf("[%s] Exited - %s catch signal %d", sTime(), pName, ws.Signal())
		}

		time.Sleep(time.Second * 2) // After release os resouce
	}
}
Beispiel #24
0
func wait_for_syscall(pid int) (exited bool, err error) {
	var waitStatus syscall.WaitStatus
	for {
		// Entering a syscall
		if err = syscall.PtraceSyscall(pid, 0); err != nil {
			return
		}

		if _, err = syscall.Wait4(pid, &waitStatus, 0, nil); err != nil {
			return
		}

		// Is it for us ?
		if waitStatus.Stopped() && waitStatus.StopSignal()&0x80 == 0x80 {
			return
		}

		if waitStatus.Exited() {
			exited = true
			return
		}
	}
}
Beispiel #25
0
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, &regs)

				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)
			}
		}
	}
}
Beispiel #26
0
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, &regs)

				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
			}
		}
	}

}
Beispiel #27
0
// childReaper is used to handle events from child processes, including child exit.
// If running as pid=1 then this means it handles zombie process reaping for orphaned children
// as well as direct child processes.
func (t *tether) childReaper() error {
	signal.Notify(t.incoming, syscall.SIGCHLD)

	/*
	   PR_SET_CHILD_SUBREAPER (since Linux 3.4)
	          If arg2 is nonzero, set the "child subreaper" attribute of the
	          calling process; if arg2 is zero, unset the attribute.  When a
	          process is marked as a child subreaper, all of the children
	          that it creates, and their descendants, will be marked as
	          having a subreaper.  In effect, a subreaper fulfills the role
	          of init(1) for its descendant processes.  Upon termination of
	          a process that is orphaned (i.e., its immediate parent has
	          already terminated) and marked as having a subreaper, the
	          nearest still living ancestor subreaper will receive a SIGCHLD
	          signal and be able to wait(2) on the process to discover its
	          termination status.
	*/
	if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, SetChildSubreaper, uintptr(1), 0); err != 0 {
		return err
	}

	log.Info("Started reaping child processes")

	go func() {
		for _ = range t.incoming {
			var status syscall.WaitStatus

			func() {
				// general resiliency
				defer recover()

				// reap until no more children to process
				for {
					log.Debugf("Inspecting children with status change")
					pid, err := syscall.Wait4(-1, &status, syscall.WNOHANG, nil)
					if pid == 0 || err == syscall.ECHILD {
						log.Debug("No more child processes to reap")
						break
					}
					if err == nil {
						if !status.Exited() && !status.Signaled() {
							log.Debugf("Received notifcation about non-exit status change for %d: %d", pid, status)
							// no reaping or exit handling required
							continue
						}

						log.Debugf("Reaped process %d, return code: %d", pid, status.ExitStatus())

						session, ok := t.removeChildPid(pid)
						if ok {
							// will be -1 if terminated due to signal
							session.ExitStatus = status.ExitStatus()
							t.handleSessionExit(session)
						} else {
							// This is an adopted zombie. The Wait4 call
							// already clean it up from the kernel
							log.Infof("Reaped zombie process PID %d\n", pid)
						}
					} else {
						log.Warnf("Wait4 got error: %v\n", err)
					}
				}
			}()
		}
	}()

	return nil
}
Beispiel #28
0
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, &regs)
				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)
	}

}
Beispiel #29
0
func Run(startChan <-chan int,
	stopChan chan struct{},
	appName string,
	appArgs []string,
	dirName string) <-chan *report.PtMonitorReport {
	log.Info("ptmon: starting...")

	sysInfo := system.GetSystemInfo()
	archName := system.MachineToArchName(sysInfo.Machine)
	syscallResolver := system.CallNumberResolver(archName)

	reportChan := make(chan *report.PtMonitorReport, 1)

	go func() {
		ptReport := &report.PtMonitorReport{
			ArchName:     string(archName),
			SyscallStats: map[string]report.SyscallStatInfo{},
		}

		syscallStats := map[int16]uint64{}
		eventChan := make(chan syscallEvent)
		doneMonitoring := make(chan int)

		var app *exec.Cmd

		go func() {
			//Ptrace is not pretty... and it requires that you do all ptrace calls from the same thread
			runtime.LockOSThread()

			var err error
			app, err = target.Start(appName, appArgs, dirName, true)
			utils.FailOn(err)
			targetPid := app.Process.Pid

			log.Debugf("ptmon: target PID ==> %d\n", targetPid)

			var wstat syscall.WaitStatus
			_, err = syscall.Wait4(targetPid, &wstat, 0, nil)
			if err != nil {
				log.Warnf("ptmon: error waiting for %d: %v\n", targetPid, err)
				doneMonitoring <- 1
			}

			log.Debugln("ptmon: initial process status =>", wstat)

			if wstat.Exited() {
				log.Warn("ptmon: app exited (unexpected)")
				doneMonitoring <- 2
			}

			if wstat.Signaled() {
				log.Warn("ptmon: app signalled (unexpected)")
				doneMonitoring <- 3
			}

			syscallReturn := false
			gotCallNum := false
			gotRetVal := false
			var callNum uint64
			var retVal uint64
			for wstat.Stopped() {
				var regs syscall.PtraceRegs

				switch syscallReturn {
				case false:
					if err := syscall.PtraceGetRegs(targetPid, &regs); err != nil {
						log.Fatalf("ptmon: PtraceGetRegs(call): %v", err)
					}

					callNum = regs.Orig_rax
					syscallReturn = true
					gotCallNum = true
				case true:
					if err := syscall.PtraceGetRegs(targetPid, &regs); err != nil {
						log.Fatalf("ptmon: PtraceGetRegs(return): %v", err)
					}

					retVal = regs.Rax
					syscallReturn = false
					gotRetVal = true
				}

				err = syscall.PtraceSyscall(targetPid, 0)
				if err != nil {
					log.Warnf("ptmon: PtraceSyscall error: %v\n", err)
					break
				}
				_, err = syscall.Wait4(targetPid, &wstat, 0, nil)
				if err != nil {
					log.Warnf("ptmon: error waiting 4 %d: %v\n", targetPid, err)
					break
				}

				if gotCallNum && gotRetVal {
					gotCallNum = false
					gotRetVal = false

					eventChan <- syscallEvent{
						callNum: int16(callNum),
						retVal:  retVal,
					}
				}
			}

			log.Infoln("ptmon: monitor is exiting... status=", wstat)
			doneMonitoring <- 0
		}()

	done:
		for {
			select {
			case rc := <-doneMonitoring:
				log.Info("ptmon: done =>", rc)
				break done
			case <-stopChan:
				log.Info("ptmon: stopping...")
				//NOTE: need a better way to stop the target app...
				if err := app.Process.Signal(syscall.SIGTERM); err != nil {
					log.Warnln("ptmon: error stopping target app =>", err)
					if err := app.Process.Kill(); err != nil {
						log.Warnln("ptmon: error killing target app =>", err)
					}
				}
				break done
			case e := <-eventChan:
				ptReport.SyscallCount++

				if _, ok := syscallStats[e.callNum]; ok {
					syscallStats[e.callNum]++
				} else {
					syscallStats[e.callNum] = 1
				}
			}
		}

		log.Debugf("ptmon: executed syscall count = %d\n", ptReport.SyscallCount)
		log.Debugf("ptmon: number of syscalls: %v\n", len(syscallStats))
		for scNum, scCount := range syscallStats {
			log.Debugf("[%v] %v = %v", scNum, syscallResolver(scNum), scCount)
			ptReport.SyscallStats[strconv.FormatInt(int64(scNum), 10)] = report.SyscallStatInfo{
				Number: scNum,
				Name:   syscallResolver(scNum),
				Count:  scCount,
			}
		}

		ptReport.SyscallNum = uint32(len(ptReport.SyscallStats))
		reportChan <- ptReport
	}()

	return reportChan
}
Beispiel #30
0
func mountExplicit(databasePath string, mountPath string, mountOptions string) error {
	if alreadyMounted(mountPath) {
		return fmt.Errorf("%v: mount path already in use", mountPath)
	}

	stat, err := os.Stat(mountPath)
	if err != nil {
		return fmt.Errorf("%v: could not stat: %v", mountPath, err)
	}
	if stat == nil {
		return fmt.Errorf("%v: mount point does not exist", mountPath)
	}
	if !stat.IsDir() {
		return fmt.Errorf("%v: mount point is not a directory", mountPath)
	}

	stat, err = os.Stat(databasePath)
	if err != nil {
		return fmt.Errorf("%v: could not stat: %v", databasePath, err)
	}
	if stat == nil {
		return fmt.Errorf("%v: database does not exist")
	}

	log.Infof(2, "spawning daemon to mount VFS for database '%v' at '%v'", databasePath, mountPath)

	args := []string{"vfs", "--database=" + databasePath, mountPath, "--options=" + mountOptions}
	daemon := exec.Command(os.Args[0], args...)

	errorPipe, err := daemon.StderrPipe()
	if err != nil {
		return fmt.Errorf("could not open standard error pipe: %v", err)
	}

	err = daemon.Start()
	if err != nil {
		return fmt.Errorf("could not start daemon: %v", err)
	}

	log.Info(2, "sleeping.")

	const halfSecond = 500000000
	time.Sleep(halfSecond)

	log.Info(2, "checking whether daemon started successfully.")

	var waitStatus syscall.WaitStatus
	var rusage syscall.Rusage
	_, err = syscall.Wait4(daemon.Process.Pid, &waitStatus, syscall.WNOHANG, &rusage)
	if err != nil {
		return fmt.Errorf("could not check daemon status: %v", err)
	}

	if waitStatus.Exited() {
		if waitStatus.ExitStatus() != 0 {
			buffer := make([]byte, 1024)
			count, err := errorPipe.Read(buffer)
			if err != nil {
				return fmt.Errorf("could not read from error pipe: %v", err)
			}

			return fmt.Errorf("virtual filesystem mount failed: %v", string(buffer[0:count]))
		}
	}

	return nil
}