diff --git a/pkg/cgroup/doc.go b/pkg/cgroup/doc.go index 00a4cd6..a828f13 100644 --- a/pkg/cgroup/doc.go +++ b/pkg/cgroup/doc.go @@ -1,5 +1,6 @@ // Package cgroup provices builder to create multiple different cgroup-v1 sub groups -// under systemd defined path (i.e. /sys/fs/cgroup) +// under systemd defined path (i.e. /sys/fs/cgroup). +// // current avaliable cgroups are cpuacct, memory, pids // not avaliable: cpu, cpuset, devices, freezer, net_cls, blkio, perf_event, net_prio, huge_tlb, rdma package cgroup diff --git a/pkg/forkexec/bench_test.go b/pkg/forkexec/bench_test.go index 6889bfb..2701646 100644 --- a/pkg/forkexec/bench_test.go +++ b/pkg/forkexec/bench_test.go @@ -1,3 +1,5 @@ +// +build linux + package forkexec import ( diff --git a/pkg/forkexec/consts.go b/pkg/forkexec/consts.go index 1337467..202212b 100644 --- a/pkg/forkexec/consts.go +++ b/pkg/forkexec/consts.go @@ -1,3 +1,5 @@ +// +build linux + package forkexec import ( diff --git a/pkg/forkexec/doc.go b/pkg/forkexec/doc.go new file mode 100644 index 0000000..bf043aa --- /dev/null +++ b/pkg/forkexec/doc.go @@ -0,0 +1,7 @@ +// Package forkexec provides interface to run a subprocess with seccomp filter, rlimit and +// containerized or ptraced. +// +// unshare cgroup namespace requires kernel >= 4.6 +// seccomp, unshare pid / user namespaces requires kernel >= 3.8 +// pipe2, dup3 requires kernel >= 2.6.27 +package forkexec diff --git a/pkg/forkexec/fork.go b/pkg/forkexec/fork.go index 74d6fe1..4ca7889 100644 --- a/pkg/forkexec/fork.go +++ b/pkg/forkexec/fork.go @@ -1,3 +1,5 @@ +// +build linux + package forkexec import ( diff --git a/pkg/forkexec/fork_child.go b/pkg/forkexec/fork_child.go index 6f5b90d..de4c24d 100644 --- a/pkg/forkexec/fork_child.go +++ b/pkg/forkexec/fork_child.go @@ -1,3 +1,5 @@ +// +build linux + package forkexec import ( diff --git a/pkg/forkexec/fork_util.go b/pkg/forkexec/fork_util.go index 6ff98aa..ba5548f 100644 --- a/pkg/forkexec/fork_util.go +++ b/pkg/forkexec/fork_util.go @@ -1,3 +1,5 @@ +// +build linux + package forkexec import ( diff --git a/pkg/forkexec/runner.go b/pkg/forkexec/runner.go index 1b1b071..ca9de81 100644 --- a/pkg/forkexec/runner.go +++ b/pkg/forkexec/runner.go @@ -1,5 +1,5 @@ -// Package forkexec provides interface to run a subprocess with seccomp filter, rlimit and -// containerized or ptraced +// +build linux + package forkexec import ( diff --git a/pkg/forkexec/userns.go b/pkg/forkexec/userns.go index 79aa8ec..f0408bd 100644 --- a/pkg/forkexec/userns.go +++ b/pkg/forkexec/userns.go @@ -1,3 +1,5 @@ +// +build linux + package forkexec import ( diff --git a/pkg/pipe/buffer.go b/pkg/pipe/buffer.go index 14ce04c..2a37475 100644 --- a/pkg/pipe/buffer.go +++ b/pkg/pipe/buffer.go @@ -1,9 +1,10 @@ // Package pipe provides a wrapper to create a pipe and -// read at most max content from the reader side +// collect at most max bytes from the reader side package pipe import ( "bytes" + "fmt" "io" "os" ) @@ -50,3 +51,7 @@ func NewBuffer(max int64) (*Buffer, error) { Done: done, }, nil } + +func (b Buffer) String() string { + return fmt.Sprintf("Buffer[%d/%d]", b.Buffer.Len(), b.Max) +} diff --git a/pkg/rlimit/rlimit.go b/pkg/rlimit/rlimit.go index efca96f..1e57609 100644 --- a/pkg/rlimit/rlimit.go +++ b/pkg/rlimit/rlimit.go @@ -1,7 +1,12 @@ +// Package rlimit provides data structure for resource limits by setrlimit syscall on linux. package rlimit import ( + "fmt" + "strings" "syscall" + + "github.com/criyle/go-sandbox/types" ) // RLimits defines the rlimit applied by setrlimit syscall to traced process @@ -67,3 +72,34 @@ func (r *RLimits) PrepareRLimit() []RLimit { } return ret } + +func (r RLimit) String() string { + if r.Res == syscall.RLIMIT_CPU { + return fmt.Sprintf("CPU[%d s:%d s]", r.Rlim.Cur, r.Rlim.Max) + } + t := "" + switch r.Res { + case syscall.RLIMIT_DATA: + t = "Data" + case syscall.RLIMIT_FSIZE: + t = "File" + case syscall.RLIMIT_STACK: + t = "Stack" + case syscall.RLIMIT_AS: + t = "AddressSpace" + } + return fmt.Sprintf("%s[%v:%v]", t, types.Size(r.Rlim.Cur), types.Size(r.Rlim.Max)) +} + +func (r RLimits) String() string { + var sb strings.Builder + sb.WriteString("RLimits[") + for i, rl := range r.PrepareRLimit() { + if i > 0 { + sb.WriteByte(',') + } + sb.WriteString(rl.String()) + } + sb.WriteString("]") + return sb.String() +} diff --git a/pkg/seccomp/filter.go b/pkg/seccomp/filter.go index fc92794..3d76ab0 100644 --- a/pkg/seccomp/filter.go +++ b/pkg/seccomp/filter.go @@ -1,22 +1,5 @@ // Package seccomp provides a generated filter format for seccomp filter - -// +build linux - package seccomp -import ( - "syscall" - "unsafe" -) - // Filter is the BPF seccomp filter value type Filter []byte - -// SockFprog converts Filter to SockFprog for seccomp syscall -func (f Filter) SockFprog() *syscall.SockFprog { - b := []byte(f) - return &syscall.SockFprog{ - Len: uint16(len(b) / 8), - Filter: (*syscall.SockFilter)(unsafe.Pointer(&b[0])), - } -} diff --git a/pkg/seccomp/filter_linux.go b/pkg/seccomp/filter_linux.go new file mode 100644 index 0000000..f7a57ca --- /dev/null +++ b/pkg/seccomp/filter_linux.go @@ -0,0 +1,15 @@ +package seccomp + +import ( + "syscall" + "unsafe" +) + +// SockFprog converts Filter to SockFprog for seccomp syscall +func (f Filter) SockFprog() *syscall.SockFprog { + b := []byte(f) + return &syscall.SockFprog{ + Len: uint16(len(b) / 8), + Filter: (*syscall.SockFilter)(unsafe.Pointer(&b[0])), + } +} diff --git a/pkg/seccomp/libseccomp/action.go b/pkg/seccomp/libseccomp/action.go index 972002c..f61c0dd 100644 --- a/pkg/seccomp/libseccomp/action.go +++ b/pkg/seccomp/libseccomp/action.go @@ -1,3 +1,5 @@ +// +build linux + package libseccomp import ( diff --git a/pkg/seccomp/libseccomp/builder.go b/pkg/seccomp/libseccomp/builder.go index 8ac6180..544f57d 100644 --- a/pkg/seccomp/libseccomp/builder.go +++ b/pkg/seccomp/libseccomp/builder.go @@ -1,4 +1,5 @@ -// Package libseccomp provides wrapper to "github.com/seccomp/libseccomp-golang" +// +build linux + package libseccomp import ( diff --git a/pkg/seccomp/libseccomp/doc.go b/pkg/seccomp/libseccomp/doc.go new file mode 100644 index 0000000..fd108b8 --- /dev/null +++ b/pkg/seccomp/libseccomp/doc.go @@ -0,0 +1,2 @@ +// Package libseccomp provides a wrapper for "github.com/seccomp/libseccomp-golang" +package libseccomp diff --git a/pkg/seccomp/libseccomp/syscallname.go b/pkg/seccomp/libseccomp/syscallname.go index 9d9e67f..6774d28 100644 --- a/pkg/seccomp/libseccomp/syscallname.go +++ b/pkg/seccomp/libseccomp/syscallname.go @@ -1,3 +1,5 @@ +// +build linux + package libseccomp import ( diff --git a/pkg/unixsocket/socket.go b/pkg/unixsocket/socket.go index 7b3f621..d7424ad 100644 --- a/pkg/unixsocket/socket.go +++ b/pkg/unixsocket/socket.go @@ -1,6 +1,9 @@ +// Package unixsocket provides wrapper for Linux unix socket to send and recv oob messages +// including fd and user credential. package unixsocket import ( + "bytes" "fmt" "net" "os" @@ -11,17 +14,15 @@ import ( // oob size default to page size const oobSize = 4096 -// use pool to avoid allocate +// use pool to minimize allocate gabage collector overhead var oobPool = sync.Pool{ New: func() interface{} { return make([]byte, oobSize) }, } -// Socket represents a unix socket -type Socket struct { - *net.UnixConn -} +// Socket wrappers a unix socket connection +type Socket net.UnixConn // Msg is the oob msg with the message type Msg struct { @@ -37,46 +38,51 @@ type Msg struct { func NewSocket(fd int) (*Socket, error) { file := os.NewFile(uintptr(fd), "unix-socket") if file == nil { - return nil, fmt.Errorf("NewSocket: fd(%d) is not a valid fd", fd) + return nil, fmt.Errorf("NewSocket: %d is not a valid fd", fd) } defer file.Close() syscall.CloseOnExec(int(file.Fd())) + conn, err := net.FileConn(file) if err != nil { return nil, err } + unixConn, ok := conn.(*net.UnixConn) if !ok { conn.Close() - return nil, fmt.Errorf("NewSocket: fd(%d) is not a unix socket", fd) + return nil, fmt.Errorf("NewSocket: %d is not a valid unix socket connection", fd) } - return &Socket{unixConn}, nil + return (*Socket)(unixConn), nil } // NewSocketPair creates connected unix socketpair using SOCK_SEQPACKET func NewSocketPair() (*Socket, *Socket, error) { fd, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET|syscall.SOCK_CLOEXEC, 0) if err != nil { - return nil, nil, fmt.Errorf("NewSocketPair: failed to call socketpair(%v)", err) + return nil, nil, fmt.Errorf("NewSocketPair: failed to call socketpair %w", err) } + ins, err := NewSocket(fd[0]) if err != nil { syscall.Close(fd[0]) syscall.Close(fd[1]) - return nil, nil, fmt.Errorf("NewSocketPair: failed to call NewSocket ins(%v)", err) + return nil, nil, fmt.Errorf("NewSocketPair: failed to call NewSocket on sender %w", err) } + outs, err := NewSocket(fd[1]) if err != nil { ins.Close() syscall.Close(fd[1]) - return nil, nil, fmt.Errorf("NewSocketPair: failed to call NewSocket outs(%v)", err) + return nil, nil, fmt.Errorf("NewSocketPair: failed to call NewSocket receiver %w", err) } + return ins, outs, nil } // SetPassCred set sockopt for pass cred for unix socket func (s *Socket) SetPassCred(option int) error { - sysconn, err := s.SyscallConn() + sysconn, err := (*net.UnixConn)(s).SyscallConn() if err != nil { return err } @@ -87,16 +93,20 @@ func (s *Socket) SetPassCred(option int) error { // SendMsg sendmsg to unix socket and encode possible unix right / credential func (s *Socket) SendMsg(b []byte, m *Msg) error { - var oob []byte + buf := oobPool.Get().([]byte) + defer oobPool.Put(buf) + + oob := bytes.NewBuffer(buf[:0]) if m != nil { if len(m.Fds) > 0 { - oob = append(oob, syscall.UnixRights(m.Fds...)...) + oob.Write(syscall.UnixRights(m.Fds...)) } if m.Cred != nil { - oob = append(oob, syscall.UnixCredentials(m.Cred)...) + oob.Write(syscall.UnixCredentials(m.Cred)) } } - _, _, err := s.WriteMsgUnix(b, oob, nil) + + _, _, err := (*net.UnixConn)(s).WriteMsgUnix(b, oob.Bytes(), nil) if err != nil { return err } @@ -107,7 +117,8 @@ func (s *Socket) SendMsg(b []byte, m *Msg) error { func (s *Socket) RecvMsg(b []byte) (int, *Msg, error) { oob := oobPool.Get().([]byte) defer oobPool.Put(oob) - n, oobn, _, _, err := s.ReadMsgUnix(b, oob) + + n, oobn, _, _, err := (*net.UnixConn)(s).ReadMsgUnix(b, oob) if err != nil { return 0, nil, err } @@ -126,23 +137,24 @@ func (s *Socket) RecvMsg(b []byte) (int, *Msg, error) { func parseMsg(msgs []syscall.SocketControlMessage) (*Msg, error) { var msg Msg for _, m := range msgs { - if m.Header.Level == syscall.SOL_SOCKET { - switch m.Header.Type { - case syscall.SCM_CREDENTIALS: - cred, err := syscall.ParseUnixCredentials(&m) - if err != nil { - return nil, err - } - msg.Cred = cred - - case syscall.SCM_RIGHTS: - fds, err := syscall.ParseUnixRights(&m) - if err != nil { - return nil, err - } - msg.Fds = fds + if m.Header.Level != syscall.SOL_SOCKET { + continue + } + switch m.Header.Type { + case syscall.SCM_CREDENTIALS: + cred, err := syscall.ParseUnixCredentials(&m) + if err != nil { + return nil, err } + msg.Cred = cred + + case syscall.SCM_RIGHTS: + fds, err := syscall.ParseUnixRights(&m) + if err != nil { + return nil, err + } + msg.Fds = fds } } return &msg, nil diff --git a/ptracer/context_helper.go b/ptracer/context_helper_linux.go similarity index 100% rename from ptracer/context_helper.go rename to ptracer/context_helper_linux.go diff --git a/ptracer/context.go b/ptracer/context_linux.go similarity index 100% rename from ptracer/context.go rename to ptracer/context_linux.go diff --git a/ptracer/context_amd64.go b/ptracer/context_linux_amd64.go similarity index 100% rename from ptracer/context_amd64.go rename to ptracer/context_linux_amd64.go diff --git a/ptracer/context_arm.go b/ptracer/context_linux_arm.go similarity index 100% rename from ptracer/context_arm.go rename to ptracer/context_linux_arm.go diff --git a/ptracer/context_arm64.go b/ptracer/context_linux_arm64.go similarity index 100% rename from ptracer/context_arm64.go rename to ptracer/context_linux_arm64.go diff --git a/ptracer/context_other.go b/ptracer/context_other.go new file mode 100644 index 0000000..742a984 --- /dev/null +++ b/ptracer/context_other.go @@ -0,0 +1,44 @@ +// +build !linux + +package ptracer + +// Context empty structure filler for other OS +type Context struct { + Pid int +} + +func (c *Context) SyscallNo() uint { + return 0 +} + +func (c *Context) Arg0() uint { + return 0 +} + +func (c *Context) Arg1() uint { + return 0 +} + +func (c *Context) Arg2() uint { + return 0 +} + +func (c *Context) Arg3() uint { + return 0 +} + +func (c *Context) Arg4() uint { + return 0 +} + +func (c *Context) Arg5() uint { + return 0 +} + +func (c *Context) SetReturnValue(retval int) { + +} + +func (c *Context) GetString(addr uintptr) string { + return "" +} diff --git a/ptracer/doc.go b/ptracer/doc.go new file mode 100644 index 0000000..26cf9fb --- /dev/null +++ b/ptracer/doc.go @@ -0,0 +1,3 @@ +// Package ptracer provides platfirm independent ptrace pooling loop +// interface to trace program syscalls on Linux. +package ptracer diff --git a/ptracer/ptrace.go b/ptracer/ptrace_linux.go similarity index 100% rename from ptracer/ptrace.go rename to ptracer/ptrace_linux.go diff --git a/ptracer/tracer_track.go b/ptracer/tracer_track_linux.go similarity index 100% rename from ptracer/tracer_track.go rename to ptracer/tracer_track_linux.go diff --git a/runner/unshare/default.go b/runner/unshare/default.go index b9c8f1a..f0d123b 100644 --- a/runner/unshare/default.go +++ b/runner/unshare/default.go @@ -1,3 +1,5 @@ +// +build linux + package unshare import ( diff --git a/runner/unshare/run.go b/runner/unshare/run.go index 0bfe47d..ce0051c 100644 --- a/runner/unshare/run.go +++ b/runner/unshare/run.go @@ -1,3 +1,5 @@ +// +build linux + package unshare import (