diff --git a/daemon/master.go b/daemon/master.go index 4594438..113ea98 100644 --- a/daemon/master.go +++ b/daemon/master.go @@ -3,6 +3,7 @@ package daemon import ( "fmt" "os" + "sync" "github.com/criyle/go-sandbox/pkg/forkexec" "github.com/criyle/go-sandbox/pkg/memfd" @@ -32,7 +33,8 @@ type Builder struct { type Master struct { pid int // underlying container init pid socket *unixsocket.Socket // master - container communication - buff *pipe.Buffer + buff *pipe.Buffer // collect stderr output from container + mu sync.Mutex // lock to avoid race condition } // Build creates new master with underlying container @@ -55,14 +57,14 @@ func (b *Builder) Build() (*Master, error) { } } // prepare stdin / stdout / stderr - fnull, err := os.OpenFile(os.DevNull, os.O_RDWR, os.ModePerm) + devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, os.ModePerm) if err != nil { return nil, fmt.Errorf("daemon: failed to open devNull(%v)", err) } - defer fnull.Close() + defer devNull.Close() files := make([]uintptr, 0, 4) - files = append(files, []uintptr{fnull.Fd(), fnull.Fd()}...) + files = append(files, devNull.Fd(), devNull.Fd()) if b.Stderr { buff, err = pipe.NewBuffer(bufferSize) if err != nil { @@ -71,7 +73,7 @@ func (b *Builder) Build() (*Master, error) { defer buff.W.Close() files = append(files, buff.W.Fd()) } else { - files = append(files, fnull.Fd()) + files = append(files, devNull.Fd()) } // prepare self memfd @@ -113,7 +115,11 @@ func (b *Builder) Build() (*Master, error) { ins.Close() return nil, fmt.Errorf("daemon: failed to execve(%v)", err) } - return &Master{pid, ins, buff}, nil + return &Master{ + pid: pid, + socket: ins, + buff: buff, + }, nil } // Destroy kill the daemon process (with container) diff --git a/daemon/master_cmd.go b/daemon/master_cmd.go index c717580..da022ec 100644 --- a/daemon/master_cmd.go +++ b/daemon/master_cmd.go @@ -3,12 +3,21 @@ package daemon import ( "fmt" "os" + "time" "github.com/criyle/go-sandbox/pkg/unixsocket" ) // Ping send ping message to container func (m *Master) Ping() error { + m.mu.Lock() + defer m.mu.Unlock() + + // avoid infinite wait (max 3s) + const pingWait = 3 * time.Second + m.socket.SetDeadline(time.Now().Add(pingWait)) + defer m.socket.SetDeadline(time.Time{}) + // send ping cmd := Cmd{ Cmd: cmdPing, @@ -22,6 +31,9 @@ func (m *Master) Ping() error { // CopyIn copies file to container func (m *Master) CopyIn(f *os.File, p string) error { + m.mu.Lock() + defer m.mu.Unlock() + // send copyin cmd := Cmd{ Cmd: cmdCopyIn, @@ -38,6 +50,9 @@ func (m *Master) CopyIn(f *os.File, p string) error { // Open open file in container func (m *Master) Open(p string) (*os.File, error) { + m.mu.Lock() + defer m.mu.Unlock() + // send copyin cmd := Cmd{ Cmd: cmdOpen, @@ -67,6 +82,9 @@ func (m *Master) Open(p string) (*os.File, error) { // Delete remove file from container func (m *Master) Delete(p string) error { + m.mu.Lock() + defer m.mu.Unlock() + cmd := Cmd{ Cmd: cmdDelete, Path: p, @@ -79,6 +97,9 @@ func (m *Master) Delete(p string) error { // Reset remove all from /tmp and /w func (m *Master) Reset() error { + m.mu.Lock() + defer m.mu.Unlock() + cmd := Cmd{ Cmd: cmdReset, } diff --git a/daemon/master_cmd_execve.go b/daemon/master_cmd_execve.go index 454bfdd..aded783 100644 --- a/daemon/master_cmd_execve.go +++ b/daemon/master_cmd_execve.go @@ -14,20 +14,26 @@ type ExecveParam struct { Args []string Env []string Fds []uintptr + // fexecve fd ExecFile uintptr + // POSIX Resource limit set by set rlimit RLimits []rlimit.RLimit - // SyncFunc called with pid before execve + + // SyncFunc called with pid before execve (e.g. add to cgroup) SyncFunc func(pid int) error } // Execve runs process inside container // accepts done for cancelation func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.Result, error) { - var files []int + m.mu.Lock() + sTime := time.Now() + // if execve with fd, put fd at the first parameter + var files []int if param.ExecFile > 0 { files = append(files, int(param.ExecFile)) } @@ -43,36 +49,46 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types. FdExec: param.ExecFile > 0, } if err := m.sendCmd(&cmd, msg); err != nil { + m.mu.Unlock() return nil, fmt.Errorf("execve: sendCmd %v", err) } reply, msg, err := m.recvReply() if err != nil { + m.mu.Unlock() return nil, fmt.Errorf("execve: recvReply %v", err) } if reply.Error != "" || msg == nil || msg.Cred == nil { m.execveSyncKill() + m.mu.Unlock() return nil, fmt.Errorf("execve: no pid recved or error(%v)", reply.Error) } if param.SyncFunc != nil { if err := param.SyncFunc(int(msg.Cred.Pid)); err != nil { m.execveSyncKill() + m.mu.Unlock() return nil, fmt.Errorf("execve: syncfunc failed(%v)", err) } } // send to syncFunc ack ok if err := m.sendCmd(&Cmd{Cmd: cmdOk}, nil); err != nil { + m.mu.Unlock() return nil, fmt.Errorf("execve: ok failed(%v)", err) } + mTime := time.Now() + // make sure goroutine not leaked (blocked) even if result is not consumed result := make(chan types.Result, 1) waitDone := make(chan struct{}) + // Wait go func() { reply2, _, _ := m.recvReply() close(waitDone) // done signal (should recv after kill) m.recvReply() + // unlock after last read / write + m.mu.Unlock() // emit result after all communication finish status := reply2.Status if reply2.Error != "" { @@ -88,6 +104,7 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types. RunningTime: time.Since(mTime), } }() + // Kill (if wait is done, a kill message need to be send to collect zombies) go func() { select { @@ -96,6 +113,7 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types. } m.sendCmd(&Cmd{Cmd: cmdKill}, nil) }() + return result, nil } diff --git a/go.mod b/go.mod index 72c131c..651bb81 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.13 require ( github.com/seccomp/libseccomp-golang v0.9.1 - golang.org/x/sys v0.0.0-20191010194322-b09406accb47 + golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 ) diff --git a/go.sum b/go.sum index 6627875..ec73c4f 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 h1:nq114VpM8lsSlP+lyUbANecYHYiFcSNFtqcBlxRV+gA= +golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=