add lock for daemon socket

This commit is contained in:
criyle 2019-10-20 21:25:53 -07:00
parent f2a28034f3
commit c244811c19
5 changed files with 56 additions and 11 deletions

View File

@ -3,6 +3,7 @@ package daemon
import ( import (
"fmt" "fmt"
"os" "os"
"sync"
"github.com/criyle/go-sandbox/pkg/forkexec" "github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/memfd" "github.com/criyle/go-sandbox/pkg/memfd"
@ -32,7 +33,8 @@ type Builder struct {
type Master struct { type Master struct {
pid int // underlying container init pid pid int // underlying container init pid
socket *unixsocket.Socket // master - container communication 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 // Build creates new master with underlying container
@ -55,14 +57,14 @@ func (b *Builder) Build() (*Master, error) {
} }
} }
// prepare stdin / stdout / stderr // 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 { if err != nil {
return nil, fmt.Errorf("daemon: failed to open devNull(%v)", err) return nil, fmt.Errorf("daemon: failed to open devNull(%v)", err)
} }
defer fnull.Close() defer devNull.Close()
files := make([]uintptr, 0, 4) files := make([]uintptr, 0, 4)
files = append(files, []uintptr{fnull.Fd(), fnull.Fd()}...) files = append(files, devNull.Fd(), devNull.Fd())
if b.Stderr { if b.Stderr {
buff, err = pipe.NewBuffer(bufferSize) buff, err = pipe.NewBuffer(bufferSize)
if err != nil { if err != nil {
@ -71,7 +73,7 @@ func (b *Builder) Build() (*Master, error) {
defer buff.W.Close() defer buff.W.Close()
files = append(files, buff.W.Fd()) files = append(files, buff.W.Fd())
} else { } else {
files = append(files, fnull.Fd()) files = append(files, devNull.Fd())
} }
// prepare self memfd // prepare self memfd
@ -113,7 +115,11 @@ func (b *Builder) Build() (*Master, error) {
ins.Close() ins.Close()
return nil, fmt.Errorf("daemon: failed to execve(%v)", err) 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) // Destroy kill the daemon process (with container)

View File

@ -3,12 +3,21 @@ package daemon
import ( import (
"fmt" "fmt"
"os" "os"
"time"
"github.com/criyle/go-sandbox/pkg/unixsocket" "github.com/criyle/go-sandbox/pkg/unixsocket"
) )
// Ping send ping message to container // Ping send ping message to container
func (m *Master) Ping() error { 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 // send ping
cmd := Cmd{ cmd := Cmd{
Cmd: cmdPing, Cmd: cmdPing,
@ -22,6 +31,9 @@ func (m *Master) Ping() error {
// CopyIn copies file to container // CopyIn copies file to container
func (m *Master) CopyIn(f *os.File, p string) error { func (m *Master) CopyIn(f *os.File, p string) error {
m.mu.Lock()
defer m.mu.Unlock()
// send copyin // send copyin
cmd := Cmd{ cmd := Cmd{
Cmd: cmdCopyIn, Cmd: cmdCopyIn,
@ -38,6 +50,9 @@ func (m *Master) CopyIn(f *os.File, p string) error {
// Open open file in container // Open open file in container
func (m *Master) Open(p string) (*os.File, error) { func (m *Master) Open(p string) (*os.File, error) {
m.mu.Lock()
defer m.mu.Unlock()
// send copyin // send copyin
cmd := Cmd{ cmd := Cmd{
Cmd: cmdOpen, Cmd: cmdOpen,
@ -67,6 +82,9 @@ func (m *Master) Open(p string) (*os.File, error) {
// Delete remove file from container // Delete remove file from container
func (m *Master) Delete(p string) error { func (m *Master) Delete(p string) error {
m.mu.Lock()
defer m.mu.Unlock()
cmd := Cmd{ cmd := Cmd{
Cmd: cmdDelete, Cmd: cmdDelete,
Path: p, Path: p,
@ -79,6 +97,9 @@ func (m *Master) Delete(p string) error {
// Reset remove all from /tmp and /w // Reset remove all from /tmp and /w
func (m *Master) Reset() error { func (m *Master) Reset() error {
m.mu.Lock()
defer m.mu.Unlock()
cmd := Cmd{ cmd := Cmd{
Cmd: cmdReset, Cmd: cmdReset,
} }

View File

@ -14,20 +14,26 @@ type ExecveParam struct {
Args []string Args []string
Env []string Env []string
Fds []uintptr Fds []uintptr
// fexecve fd // fexecve fd
ExecFile uintptr ExecFile uintptr
// POSIX Resource limit set by set rlimit // POSIX Resource limit set by set rlimit
RLimits []rlimit.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 SyncFunc func(pid int) error
} }
// Execve runs process inside container // Execve runs process inside container
// accepts done for cancelation // accepts done for cancelation
func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.Result, error) { func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.Result, error) {
var files []int m.mu.Lock()
sTime := time.Now() sTime := time.Now()
// if execve with fd, put fd at the first parameter
var files []int
if param.ExecFile > 0 { if param.ExecFile > 0 {
files = append(files, int(param.ExecFile)) 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, FdExec: param.ExecFile > 0,
} }
if err := m.sendCmd(&cmd, msg); err != nil { if err := m.sendCmd(&cmd, msg); err != nil {
m.mu.Unlock()
return nil, fmt.Errorf("execve: sendCmd %v", err) return nil, fmt.Errorf("execve: sendCmd %v", err)
} }
reply, msg, err := m.recvReply() reply, msg, err := m.recvReply()
if err != nil { if err != nil {
m.mu.Unlock()
return nil, fmt.Errorf("execve: recvReply %v", err) return nil, fmt.Errorf("execve: recvReply %v", err)
} }
if reply.Error != "" || msg == nil || msg.Cred == nil { if reply.Error != "" || msg == nil || msg.Cred == nil {
m.execveSyncKill() m.execveSyncKill()
m.mu.Unlock()
return nil, fmt.Errorf("execve: no pid recved or error(%v)", reply.Error) return nil, fmt.Errorf("execve: no pid recved or error(%v)", reply.Error)
} }
if param.SyncFunc != nil { if param.SyncFunc != nil {
if err := param.SyncFunc(int(msg.Cred.Pid)); err != nil { if err := param.SyncFunc(int(msg.Cred.Pid)); err != nil {
m.execveSyncKill() m.execveSyncKill()
m.mu.Unlock()
return nil, fmt.Errorf("execve: syncfunc failed(%v)", err) return nil, fmt.Errorf("execve: syncfunc failed(%v)", err)
} }
} }
// send to syncFunc ack ok // send to syncFunc ack ok
if err := m.sendCmd(&Cmd{Cmd: cmdOk}, nil); err != nil { if err := m.sendCmd(&Cmd{Cmd: cmdOk}, nil); err != nil {
m.mu.Unlock()
return nil, fmt.Errorf("execve: ok failed(%v)", err) return nil, fmt.Errorf("execve: ok failed(%v)", err)
} }
mTime := time.Now() mTime := time.Now()
// make sure goroutine not leaked (blocked) even if result is not consumed // make sure goroutine not leaked (blocked) even if result is not consumed
result := make(chan types.Result, 1) result := make(chan types.Result, 1)
waitDone := make(chan struct{}) waitDone := make(chan struct{})
// Wait // Wait
go func() { go func() {
reply2, _, _ := m.recvReply() reply2, _, _ := m.recvReply()
close(waitDone) close(waitDone)
// done signal (should recv after kill) // done signal (should recv after kill)
m.recvReply() m.recvReply()
// unlock after last read / write
m.mu.Unlock()
// emit result after all communication finish // emit result after all communication finish
status := reply2.Status status := reply2.Status
if reply2.Error != "" { if reply2.Error != "" {
@ -88,6 +104,7 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
RunningTime: time.Since(mTime), RunningTime: time.Since(mTime),
} }
}() }()
// Kill (if wait is done, a kill message need to be send to collect zombies) // Kill (if wait is done, a kill message need to be send to collect zombies)
go func() { go func() {
select { select {
@ -96,6 +113,7 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
} }
m.sendCmd(&Cmd{Cmd: cmdKill}, nil) m.sendCmd(&Cmd{Cmd: cmdKill}, nil)
}() }()
return result, nil return result, nil
} }

2
go.mod
View File

@ -4,5 +4,5 @@ go 1.13
require ( require (
github.com/seccomp/libseccomp-golang v0.9.1 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
) )

4
go.sum
View File

@ -1,4 +1,4 @@
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= 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-20191020212454-3e7259c5e7c2 h1:nq114VpM8lsSlP+lyUbANecYHYiFcSNFtqcBlxRV+gA=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=