normalize naming schema

This commit is contained in:
criyle 2019-08-30 00:57:07 -07:00
parent ebab7514d1
commit 7b06ce65ac
38 changed files with 269 additions and 258 deletions

View File

@ -54,21 +54,21 @@ Default file access syscall check:
## Packages
- tracer: ptrace tracer and provides syscall trap filter context
- deamon: creates pre-forked container to run programs inside
- runprogram: wrapper to call forkexec and trecer
- rununshared: wrapper to call forkexec and unshared namespaces
- runconfig: defines arch & language specified trace condition for seccomp and ptrace
- types: general runtime specs
- specs: provides general res / result data structures
- runner: interface to run program
- config: defines arch & language specified trace condition for seccomp and ptrace
- ptrace: wrapper to call forkexec and ptracer
- unshare: wrapper to call forkexec and unshared namespaces
- ptracer: ptrace tracer and provides syscall trap filter context
- types: provides general res / result data structures
## Executable
- run_program: safely run program by unshare / ptrace / pre-forked containers
- runprog: safely run program by unshare / ptrace / pre-forked containers
## Configurations
- run_program/config.go: all configs toward running specs
- run/config/config.go: all configs toward running specs
## Benchmarks (docker desktop amd64 / native arm64)

View File

@ -12,10 +12,11 @@ import (
"github.com/criyle/go-sandbox/pkg/cgroup"
"github.com/criyle/go-sandbox/pkg/memfd"
"github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/runconfig"
"github.com/criyle/go-sandbox/runprogram"
"github.com/criyle/go-sandbox/rununshared"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/runner"
"github.com/criyle/go-sandbox/runner/config"
"github.com/criyle/go-sandbox/runner/ptrace"
"github.com/criyle/go-sandbox/runner/unshare"
"github.com/criyle/go-sandbox/types"
)
const (
@ -33,11 +34,6 @@ var (
args []string
)
// Runner can be ptraced runner or namespaced runner
type Runner interface {
Start(<-chan struct{}) (<-chan specs.TraceResult, error)
}
func printUsage() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] <args>\n", os.Args[0])
flag.PrintDefaults()
@ -104,28 +100,28 @@ func main() {
defer f.Close()
}
rt, err := run()
rt, err := start()
if rt == nil {
rt = &specs.TraceResult{
TraceStatus: specs.TraceCodeFatal,
rt = &types.Result{
Status: types.StatusFatal,
}
}
if err == nil && rt.TraceStatus != specs.TraceCodeNormal {
err = rt.TraceStatus
if err == nil && rt.Status != types.StatusNormal {
err = rt.Status
}
if err != nil {
debug(err)
c, ok := err.(specs.TraceCode)
c, ok := err.(types.Status)
if !ok {
c = specs.TraceCodeFatal
c = types.StatusFatal
}
// Handle fatal error from trace
fmt.Fprintf(f, "%d %d %d %d\n", int(c), rt.UserTime, rt.UserMem, rt.ExitCode)
if c == specs.TraceCodeFatal {
fmt.Fprintf(f, "%d %d %d %d\n", int(c), rt.UserTime, rt.UserMem, rt.ExitStatus)
if c == types.StatusFatal {
os.Exit(1)
}
} else {
fmt.Fprintf(f, "%d %d %d %d\n", 0, rt.UserTime, rt.UserMem, rt.ExitCode)
fmt.Fprintf(f, "%d %d %d %d\n", 0, rt.UserTime, rt.UserMem, rt.ExitStatus)
}
}
@ -134,22 +130,22 @@ type deamonRunner struct {
*deamon.ExecveParam
}
func (r *deamonRunner) Start(done <-chan struct{}) (<-chan specs.TraceResult, error) {
func (r *deamonRunner) Start(done <-chan struct{}) (<-chan types.Result, error) {
return r.Master.Execve(done, r.ExecveParam)
}
func run() (*specs.TraceResult, error) {
func start() (*types.Result, error) {
var (
runner Runner
runner runner.Runner
cg *cgroup.CGroup
err error
execFile uintptr
rt specs.TraceResult
rt types.Result
)
addRead := runconfig.GetExtraSet(addReadable, addRawReadable)
addWrite := runconfig.GetExtraSet(addWritable, addRawWritable)
h := runconfig.GetConf(pType, workPath, args, addRead, addWrite, allowProc, showDetails)
addRead := config.GetExtraSet(addReadable, addRawReadable)
addWrite := config.GetExtraSet(addWritable, addRawWritable)
h := config.GetConf(pType, workPath, args, addRead, addWrite, allowProc, showDetails)
if useCGroup {
cg, err = cgroup.NewCGroup("run_program")
@ -245,21 +241,20 @@ func run() (*specs.TraceResult, error) {
}
defer os.RemoveAll(root)
runner = &rununshared.RunUnshared{
runner = &unshare.Runner{
Args: h.Args,
Env: []string{pathEnv},
ExecFile: execFile,
WorkDir: "/w",
Files: fds,
RLimits: rlims,
ResLimits: specs.ResLimit{
ResLimits: types.Limit{
TimeLimit: timeLimit * 1e3,
RealTimeLimit: realTimeLimit * 1e3,
MemoryLimit: memoryLimit << 10,
},
SyscallAllowed: h.SyscallAllow,
Root: root,
Mounts: rununshared.GetDefaultMounts(root, []rununshared.AddBind{
Mounts: unshare.GetDefaultMounts(root, []unshare.AddBind{
{
Source: workPath,
Target: "w",
@ -271,15 +266,14 @@ func run() (*specs.TraceResult, error) {
DomainName: "run_program",
}
} else {
runner = &runprogram.RunProgram{
runner = &ptrace.Runner{
Args: h.Args,
Env: []string{pathEnv},
ExecFile: execFile,
WorkDir: workPath,
RLimits: rlims,
TraceLimit: specs.ResLimit{
TraceLimit: types.Limit{
TimeLimit: timeLimit * 1e3,
RealTimeLimit: realTimeLimit * 1e3,
MemoryLimit: memoryLimit << 10,
},
Files: fds,
@ -309,7 +303,7 @@ func run() (*specs.TraceResult, error) {
case <-sig:
close(done)
rt = <-s
rt.TraceStatus = specs.TraceCodeFatal
rt.Status = types.StatusFatal
case <-tC:
close(done)
@ -320,8 +314,8 @@ func run() (*specs.TraceResult, error) {
eTime := time.Now()
if rt.SetUpTime == 0 {
rt.SetUpTime = int64(rTime.Sub(sTime))
rt.RunningTime = int64(eTime.Sub(rTime))
rt.SetUpTime = rTime.Sub(sTime)
rt.RunningTime = eTime.Sub(rTime)
}
debug("results:", rt, err)

View File

@ -10,7 +10,7 @@ import (
"github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/unixsocket"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/types"
)
// ContainerInit is called for container init process
@ -231,19 +231,19 @@ loop:
break loop
case wstatus.Signaled():
var status specs.TraceCode
var status types.Status
switch wstatus.Signal() {
// kill signal treats as TLE
case syscall.SIGXCPU, syscall.SIGKILL:
status = specs.TraceCodeTLE
status = types.StatusTLE
case syscall.SIGXFSZ:
status = specs.TraceCodeOLE
status = types.StatusOLE
case syscall.SIGSYS:
status = specs.TraceCodeBan
status = types.StatusBan
default:
status = specs.TraceCodeRE
status = types.StatusRE
}
reply := Reply{TraceStatus: status}
reply := Reply{Status: status}
sendReply(s, &reply, nil)
break loop
}

View File

@ -35,7 +35,7 @@ Any socket related error will cause the deamon exit (with all process inside con
import (
"github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/types"
)
// Cmd is the control message send into deamon
@ -52,5 +52,5 @@ type Cmd struct {
type Reply struct {
Error string // empty if no error
ExitStatus int // waitpid exit status
TraceStatus specs.TraceCode // TraceCode
Status types.Status // return status
}

View File

@ -4,8 +4,8 @@ import (
"fmt"
"github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/pkg/unixsocket"
"github.com/criyle/go-sandbox/types"
)
// ExecveParam is parameters to run process inside container
@ -23,7 +23,7 @@ type ExecveParam struct {
// Execve runs process inside container
// accepts done for cancelation
func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan specs.TraceResult, error) {
func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.Result, error) {
var files []int
if param.ExecFile > 0 {
files = append(files, int(param.ExecFile))
@ -60,16 +60,16 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan specs.
return nil, fmt.Errorf("execve: ok failed(%v)", err)
}
// make sure goroutine not leaked (blocked) even if result is not consumed
wait := make(chan specs.TraceResult, 1)
wait := make(chan types.Result, 1)
waitDone := make(chan struct{})
// Wait
go func() {
defer close(wait)
reply2, _, _ := m.recvReply()
close(waitDone)
wait <- specs.TraceResult{
ExitCode: reply2.ExitStatus,
TraceStatus: reply2.TraceStatus,
wait <- types.Result{
ExitStatus: reply2.ExitStatus,
Status: reply2.Status,
}
// done signal (should recv after kill)
m.recvReply()

2
go.mod
View File

@ -4,5 +4,5 @@ go 1.12
require (
github.com/seccomp/libseccomp-golang v0.9.1
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad
)

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/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad h1:cCejgArrk10gX6kFqjWeLwXD7aVMqWoRpyUCaaJSggc=
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -9,7 +9,7 @@ import (
"github.com/criyle/go-sandbox/pkg/rlimit"
)
// Runner is the RunProgramConfig including the exec path, argv
// Runner is the runptraceConfig including the exec path, argv
// and resource limits. It creates tracee for ptrace-based tracer.
// It can also create unshared process in another namespace
type Runner struct {

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
import (
"os"

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
import (
"syscall"

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
import (
"syscall"

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
import (
unix "golang.org/x/sys/unix"

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
import (
"syscall"

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
import (
"fmt"

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
import (
"syscall"

View File

@ -1,4 +1,4 @@
package tracer
package ptracer
// TraceAction defines the action returned by TraceHandle
type TraceAction int

View File

@ -1,11 +1,12 @@
package tracer
package ptracer
import (
"runtime"
"time"
"github.com/criyle/go-sandbox/types/specs"
unix "golang.org/x/sys/unix"
"github.com/criyle/go-sandbox/types"
)
// MsgDisallow, Msghandle defines the action needed when traped by
@ -16,9 +17,9 @@ const (
)
// Trace starts new goroutine and trace runner with ptrace
func Trace(done <-chan struct{}, handler Handler, runner Runner, limits specs.ResLimit) (<-chan specs.TraceResult, error) {
func Trace(done <-chan struct{}, handler Handler, runner Runner, limits types.Limit) (<-chan types.Result, error) {
var err error
result := make(chan specs.TraceResult, 1)
result := make(chan types.Result, 1)
start := make(chan struct{})
finish := make(chan struct{})
@ -40,7 +41,7 @@ func Trace(done <-chan struct{}, handler Handler, runner Runner, limits specs.Re
// TraceRun start and traces all child process by runner in the calling goroutine
// parameter done used to cancel work, start is used notify child starts
func TraceRun(done <-chan struct{}, start chan<- struct{},
handler Handler, runner Runner, limits specs.ResLimit) (result specs.TraceResult, err error) {
handler Handler, runner Runner, limits types.Limit) (result types.Result, err error) {
var (
wstatus unix.WaitStatus // wait4 wait status
rusage unix.Rusage // wait4 rusage
@ -61,7 +62,7 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
handler.Debug("tracer started: ", pgid, err)
if err != nil {
handler.Debug("start tracee failed: ", err)
result.TraceStatus = specs.TraceCodeRE
result.Status = types.StatusRE
return result, err
}
@ -83,17 +84,17 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
// also ensure processes was well terminated
defer func() {
if tle {
err = specs.TraceCodeTLE
err = types.StatusTLE
}
if err2 := recover(); err2 != nil {
handler.Debug(err2)
err = specs.TraceCodeFatal
err = types.StatusFatal
}
// kill all tracee upon return
killAll(pgid)
collectZombie(pgid)
result.SetUpTime = fTime.Sub(sTime).Nanoseconds()
result.RunningTime = time.Since(fTime).Nanoseconds()
result.SetUpTime = fTime.Sub(sTime)
result.RunningTime = time.Since(fTime)
}()
// trace unixs
@ -107,11 +108,11 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
}
if err != nil {
handler.Debug("wait4 failed: ", err)
return result, specs.TraceCodeFatal
return result, types.StatusFatal
}
handler.Debug("------ ", pid, " ------")
status := specs.TraceCodeNormal
status := types.StatusNormal
if pid == pgid {
// update resource usage and check against limits
userTime := uint64(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms
@ -119,17 +120,17 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
// check tle / mle
if userTime > limits.TimeLimit {
status = specs.TraceCodeTLE
status = types.StatusTLE
}
if userMem > limits.MemoryLimit {
status = specs.TraceCodeMLE
status = types.StatusMLE
}
result = specs.TraceResult{
result = types.Result{
Status: status,
UserTime: userTime,
UserMem: userMem,
TraceStatus: status,
}
if status != specs.TraceCodeNormal {
if status != types.StatusNormal {
return result, status
}
}
@ -141,11 +142,11 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
handler.Debug("process exited: ", pid, wstatus.ExitStatus())
if pid == pgid {
if execved {
result.ExitCode = wstatus.ExitStatus()
result.ExitStatus = wstatus.ExitStatus()
return result, nil
}
result.TraceStatus = specs.TraceCodeFatal
return result, specs.TraceCodeFatal
result.Status = types.StatusFatal
return result, types.StatusFatal
}
case wstatus.Signaled():
@ -155,15 +156,15 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
delete(traced, pid)
switch sig {
case unix.SIGXCPU, unix.SIGKILL:
status = specs.TraceCodeTLE
status = types.StatusTLE
case unix.SIGXFSZ:
status = specs.TraceCodeOLE
status = types.StatusOLE
case unix.SIGSYS:
status = specs.TraceCodeBan
status = types.StatusBan
default:
status = specs.TraceCodeRE
status = types.StatusRE
}
result.TraceStatus = status
result.Status = status
return result, status
}
unix.PtraceCont(pid, int(sig))
@ -176,7 +177,7 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
// Ptrace set option valid if the tracee is stopped
err = setPtraceOption(pid)
if err != nil {
result.TraceStatus = specs.TraceCodeFatal
result.Status = types.StatusFatal
return result, err
}
}
@ -189,7 +190,7 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
// give the customized handle for syscall
err := handleTrap(handler, pid)
if err != nil {
result.TraceStatus = specs.TraceCodeBan
result.Status = types.StatusBan
return result, err
}
} else {
@ -218,12 +219,12 @@ func TraceRun(done <-chan struct{}, start chan<- struct{},
// check if cpu rlimit hit
switch stopSig {
case unix.SIGXCPU:
status = specs.TraceCodeTLE
status = types.StatusTLE
case unix.SIGXFSZ:
status = specs.TraceCodeOLE
status = types.StatusOLE
}
if status != specs.TraceCodeNormal {
result.TraceStatus = status
if status != types.StatusNormal {
result.Status = status
return result, status
}
// Likely encountered SIGSEGV (segment violation)
@ -272,7 +273,7 @@ func handleTrap(handler Handler, pid int) error {
return ctx.skipSyscall()
case TraceKill:
return specs.TraceCodeBan
return types.StatusBan
}
}

View File

@ -1,4 +1,4 @@
package runconfig
package config
// This file includes configs for the run program settings
@ -105,7 +105,7 @@ var (
// config for different type of program
// workpath and arg0 have additional read / stat permission
runprogramConfig = map[string]ProgramConfig{
runptraceConfig = map[string]ProgramConfig{
"python2.7": ProgramConfig{
Syscall: SyscallConfig{
ExtraAllow: []string{

View File

@ -1,4 +1,4 @@
package runconfig
package config
// This file includes configs for the run program settings

View File

@ -1,4 +1,4 @@
package runconfig
package config
// This file includes configs for the run program settings

View File

@ -1,4 +1,4 @@
package runconfig
package config
// This file includes configs for the run program settings

View File

@ -1,4 +1,4 @@
package runconfig
package config
// GetConf return file access check set, syscall counter, allow and traced syscall arrays and new args
func GetConf(pType, workPath string, args, addRead, addWrite []string, allowProc, showDetails bool) *Handler {
@ -18,7 +18,7 @@ func GetConf(pType, workPath string, args, addRead, addWrite []string, allowProc
fs.Readable.AddRange(addRead, workPath)
fs.Writable.AddRange(addWrite, workPath)
if c, o := runprogramConfig[pType]; o {
if c, o := runptraceConfig[pType]; o {
allow = append(allow, c.Syscall.ExtraAllow...)
trace = append(trace, c.Syscall.ExtraBan...)
sc.AddRange(c.Syscall.ExtraCount)

View File

@ -1,4 +1,4 @@
package runconfig
package config
// ProgramConfig defines the extra config apply to program type
type ProgramConfig struct {

View File

@ -1,4 +1,4 @@
package runconfig
package config
import (
"path/filepath"

View File

@ -1,13 +1,13 @@
package runconfig
package config
import (
"fmt"
"os"
"github.com/criyle/go-sandbox/runprogram"
"github.com/criyle/go-sandbox/runner/ptrace"
)
// Handler defines file access restricted handler to call the runprogram
// Handler defines file access restricted handler to call the ptrace
// safe runner
type Handler struct {
SyscallAllow, SyscallTrace, Args []string
@ -17,51 +17,51 @@ type Handler struct {
}
// CheckRead checks whether the file have read permission
func (h *Handler) CheckRead(fn string) runprogram.TraceAction {
func (h *Handler) CheckRead(fn string) ptrace.TraceAction {
if !h.FileSet.IsReadableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
return ptrace.TraceAllow
}
// CheckWrite checks whether the file have write permission
func (h *Handler) CheckWrite(fn string) runprogram.TraceAction {
func (h *Handler) CheckWrite(fn string) ptrace.TraceAction {
if !h.FileSet.IsWritableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
return ptrace.TraceAllow
}
// CheckStat checks whether the file have stat permission
func (h *Handler) CheckStat(fn string) runprogram.TraceAction {
func (h *Handler) CheckStat(fn string) ptrace.TraceAction {
if !h.FileSet.IsStatableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
return ptrace.TraceAllow
}
// CheckSyscall checks syscalls other than allowed and traced agianst the
// SyscallCounter
func (h *Handler) CheckSyscall(syscallName string) runprogram.TraceAction {
func (h *Handler) CheckSyscall(syscallName string) ptrace.TraceAction {
// if it is traced, then try to count syscall
if inside, allow := h.SyscallCounter.Check(syscallName); inside {
if allow {
return runprogram.TraceAllow
return ptrace.TraceAllow
}
return runprogram.TraceKill
return ptrace.TraceKill
}
// if it is traced but not counted, it should be soft banned
return runprogram.TraceBan
return ptrace.TraceBan
}
// onDgsFileDetect soft ban file if in soft ban set
// otherwise stops the trace process
func (h *Handler) onDgsFileDetect(name string) runprogram.TraceAction {
func (h *Handler) onDgsFileDetect(name string) ptrace.TraceAction {
if h.FileSet.IsSoftBanFile(name) {
return runprogram.TraceBan
return ptrace.TraceBan
}
h.print("Dangerous fileopen: ", name)
return runprogram.TraceKill
return ptrace.TraceKill
}
// print is used to print debug information

View File

@ -1,4 +1,4 @@
package runconfig
package config
// SyscallCounter defines a count-down for each each syscall occurs
type SyscallCounter map[string]int

View File

@ -1,4 +1,4 @@
package runprogram
package ptrace
import (
"fmt"
@ -8,8 +8,8 @@ import (
libseccomp "github.com/seccomp/libseccomp-golang"
"github.com/criyle/go-sandbox/tracer"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/ptracer"
"github.com/criyle/go-sandbox/types"
)
type tracerHandler struct {
@ -23,11 +23,11 @@ func (h *tracerHandler) Debug(v ...interface{}) {
}
}
func (h *tracerHandler) getString(ctx *tracer.Context, addr uint) string {
func (h *tracerHandler) getString(ctx *ptracer.Context, addr uint) string {
return absPath(ctx.Pid, ctx.GetString(uintptr(addr)))
}
func (h *tracerHandler) checkOpen(ctx *tracer.Context, addr uint, flags uint) TraceAction {
func (h *tracerHandler) checkOpen(ctx *ptracer.Context, addr uint, flags uint) TraceAction {
fn := h.getString(ctx, addr)
isReadOnly := (flags&syscall.O_ACCMODE == syscall.O_RDONLY) &&
(flags&syscall.O_CREAT == 0) &&
@ -41,25 +41,25 @@ func (h *tracerHandler) checkOpen(ctx *tracer.Context, addr uint, flags uint) Tr
return h.Handler.CheckWrite(fn)
}
func (h *tracerHandler) checkRead(ctx *tracer.Context, addr uint) TraceAction {
func (h *tracerHandler) checkRead(ctx *ptracer.Context, addr uint) TraceAction {
fn := h.getString(ctx, addr)
h.Debug("check read: ", fn)
return h.Handler.CheckRead(fn)
}
func (h *tracerHandler) checkWrite(ctx *tracer.Context, addr uint) TraceAction {
func (h *tracerHandler) checkWrite(ctx *ptracer.Context, addr uint) TraceAction {
fn := h.getString(ctx, addr)
h.Debug("check write: ", fn)
return h.Handler.CheckWrite(fn)
}
func (h *tracerHandler) checkStat(ctx *tracer.Context, addr uint) TraceAction {
func (h *tracerHandler) checkStat(ctx *ptracer.Context, addr uint) TraceAction {
fn := h.getString(ctx, addr)
h.Debug("check stat: ", fn)
return h.Handler.CheckStat(fn)
}
func (h *tracerHandler) Handle(ctx *tracer.Context) tracer.TraceAction {
func (h *tracerHandler) Handle(ctx *ptracer.Context) ptracer.TraceAction {
var (
action TraceAction
syscallNo = ctx.SyscallNo()
@ -108,30 +108,30 @@ func (h *tracerHandler) Handle(ctx *tracer.Context) tracer.TraceAction {
switch action {
case TraceAllow:
return tracer.TraceAllow
return ptracer.TraceAllow
case TraceBan:
h.Debug("<soft ban syscall>")
return softBanSyscall(ctx)
default:
return tracer.TraceKill
return ptracer.TraceKill
}
}
func (h *tracerHandler) GetSyscallName(ctx *tracer.Context) (string, error) {
func (h *tracerHandler) GetSyscallName(ctx *ptracer.Context) (string, error) {
syscallNo := ctx.SyscallNo()
return libseccomp.ScmpSyscall(syscallNo).GetName()
}
func (h *tracerHandler) HandlerDisallow(name string) error {
if !h.Unsafe {
return specs.TraceCodeBan
return types.StatusBan
}
return nil
}
func softBanSyscall(ctx *tracer.Context) tracer.TraceAction {
func softBanSyscall(ctx *ptracer.Context) ptracer.TraceAction {
ctx.SetReturnValue(-int(BanRet))
return tracer.TraceBan
return ptracer.TraceBan
}
func getFileMode(flags uint) string {

View File

@ -1,16 +1,16 @@
package runprogram
package ptrace
import (
libseccomp "github.com/seccomp/libseccomp-golang"
"github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/seccomp"
"github.com/criyle/go-sandbox/tracer"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/ptracer"
"github.com/criyle/go-sandbox/types"
)
// Start starts the tracing process
func (r *RunProgram) Start(done <-chan struct{}) (<-chan specs.TraceResult, error) {
func (r *Runner) Start(done <-chan struct{}) (<-chan types.Result, error) {
// build seccomp filter
filter, err := buildFilter(r.ShowDetails, r.SyscallAllowed, r.SyscallTraced)
if err != nil {
@ -42,7 +42,7 @@ func (r *RunProgram) Start(done <-chan struct{}) (<-chan specs.TraceResult, erro
Unsafe: r.Unsafe,
Handler: r.Handler,
}
return tracer.Trace(done, th, ch, specs.ResLimit(r.TraceLimit))
return ptracer.Trace(done, th, ch, types.Limit(r.TraceLimit))
}
// build filter builds the libseccomp filter according to the allow, trace and show details
@ -51,9 +51,9 @@ func buildFilter(showDetails bool, allow, trace []string) (*libseccomp.ScmpFilte
var defaultAction libseccomp.ScmpAction
// if debug, allow all syscalls and output what was blocked
if showDetails {
defaultAction = libseccomp.ActTrace.SetReturnCode(tracer.MsgDisallow)
defaultAction = libseccomp.ActTrace.SetReturnCode(ptracer.MsgDisallow)
} else {
defaultAction = libseccomp.ActKill
}
return seccomp.BuildFilter(defaultAction, libseccomp.ActTrace.SetReturnCode(tracer.MsgHandle), allow, trace)
return seccomp.BuildFilter(defaultAction, libseccomp.ActTrace.SetReturnCode(ptracer.MsgHandle), allow, trace)
}

View File

@ -1,14 +1,14 @@
package runprogram
package ptrace
import (
"syscall"
"github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/types"
)
// RunProgram defines the spec to run a program safely
type RunProgram struct {
// Runner defines the spec to run a program safely by ptracer
type Runner struct {
// argv and env for the child process
// work path set by setcwd (current working directory for child)
Args []string
@ -25,7 +25,7 @@ type RunProgram struct {
RLimits rlimit.RLimits
// Res limit enforced by tracer
TraceLimit specs.ResLimit
TraceLimit types.Limit
// Allowed / Traced syscall names
// Notice: file access syscalls should be traced

8
runner/runner.go Normal file
View File

@ -0,0 +1,8 @@
package runner
import "github.com/criyle/go-sandbox/types"
// Runner interface defines method to start running
type Runner interface {
Start(<-chan struct{}) (<-chan types.Result, error)
}

View File

@ -1,10 +1,11 @@
package rununshared
package unshare
import (
"os"
"github.com/criyle/go-sandbox/pkg/mount"
"golang.org/x/sys/unix"
"github.com/criyle/go-sandbox/pkg/mount"
)
// AddBind is the additional bind mounts besides the default one

View File

@ -1,15 +1,16 @@
package rununshared
package unshare
import (
"fmt"
"os"
"time"
"github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/seccomp"
"github.com/criyle/go-sandbox/types/specs"
libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix"
"github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/seccomp"
"github.com/criyle/go-sandbox/types"
)
const (
@ -18,7 +19,7 @@ const (
)
// Start starts the unshared process
func (r *RunUnshared) Start(done <-chan struct{}) (<-chan specs.TraceResult, error) {
func (r *Runner) Start(done <-chan struct{}) (<-chan types.Result, error) {
filter, err := seccomp.BuildFilter(libseccomp.ActKill, libseccomp.ActTrap, r.SyscallAllowed, []string{})
if err != nil {
println(err)
@ -51,7 +52,7 @@ func (r *RunUnshared) Start(done <-chan struct{}) (<-chan specs.TraceResult, err
SyncFunc: r.SyncFunc,
}
result := make(chan specs.TraceResult, 1)
result := make(chan types.Result, 1)
start := make(chan struct{})
finish := make(chan struct{})
@ -71,13 +72,13 @@ func (r *RunUnshared) Start(done <-chan struct{}) (<-chan specs.TraceResult, err
}
// Trace tracks child processes
func (r *RunUnshared) Trace(done <-chan struct{}, start chan<- struct{},
runner *forkexec.Runner) (result specs.TraceResult, err error) {
func (r *Runner) Trace(done <-chan struct{}, start chan<- struct{},
runner *forkexec.Runner) (result types.Result, err error) {
var (
wstatus unix.WaitStatus // wait4 wait status
rusage unix.Rusage // wait4 rusage
tle = false
status = specs.TraceCodeNormal
status = types.StatusNormal
sTime = time.Now() // start time
fTime time.Time // finish time for setup
)
@ -86,7 +87,7 @@ func (r *RunUnshared) Trace(done <-chan struct{}, start chan<- struct{},
pgid, err := runner.Start()
r.println("Starts: ", pgid, err)
if err != nil {
result.TraceStatus = specs.TraceCodeRE
result.Status = types.StatusRE
return result, err
}
@ -106,13 +107,13 @@ func (r *RunUnshared) Trace(done <-chan struct{}, start chan<- struct{},
defer func() {
if tle {
err = specs.TraceCodeTLE
err = types.StatusTLE
}
// kill all tracee upon return
killAll(pgid)
collectZombie(pgid)
result.SetUpTime = fTime.Sub(sTime).Nanoseconds()
result.RunningTime = time.Since(fTime).Nanoseconds()
result.SetUpTime = fTime.Sub(sTime)
result.RunningTime = time.Since(fTime)
}()
fTime = time.Now()
@ -120,7 +121,7 @@ func (r *RunUnshared) Trace(done <-chan struct{}, start chan<- struct{},
_, err := unix.Wait4(pgid, &wstatus, 0, &rusage)
r.println("wait4: ", wstatus)
if err != nil {
return result, specs.TraceCodeFatal
return result, types.StatusFatal
}
// update resource usage and check against limits
@ -129,37 +130,37 @@ func (r *RunUnshared) Trace(done <-chan struct{}, start chan<- struct{},
// check tle / mle
if userTime > r.ResLimits.TimeLimit {
status = specs.TraceCodeTLE
status = types.StatusTLE
}
if userMem > r.ResLimits.MemoryLimit {
status = specs.TraceCodeMLE
status = types.StatusMLE
}
result = specs.TraceResult{
result = types.Result{
Status: status,
UserTime: userTime,
UserMem: userMem,
TraceStatus: status,
}
if status != specs.TraceCodeNormal {
if status != types.StatusNormal {
return result, status
}
switch {
case wstatus.Exited():
result.ExitCode = wstatus.ExitStatus()
result.ExitStatus = wstatus.ExitStatus()
return result, nil
case wstatus.Signaled():
sig := wstatus.Signal()
switch sig {
case unix.SIGXCPU, unix.SIGKILL:
status = specs.TraceCodeTLE
status = types.StatusTLE
case unix.SIGXFSZ:
status = specs.TraceCodeOLE
status = types.StatusOLE
case unix.SIGSYS:
status = specs.TraceCodeBan
status = types.StatusBan
default:
status = specs.TraceCodeRE
status = types.StatusRE
}
result.TraceStatus = status
result.Status = status
return result, status
}
}
@ -182,7 +183,7 @@ func collectZombie(pgid int) {
}
}
func (r *RunUnshared) println(v ...interface{}) {
func (r *Runner) println(v ...interface{}) {
if r.ShowDetails {
fmt.Fprintln(os.Stderr, v...)
}

View File

@ -1,13 +1,13 @@
package rununshared
package unshare
import (
"github.com/criyle/go-sandbox/pkg/mount"
"github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/types/specs"
"github.com/criyle/go-sandbox/types"
)
// RunUnshared runs program in unshared namespaces
type RunUnshared struct {
// Runner runs program in unshared namespaces
type Runner struct {
// argv and env for the child process
Args []string
Env []string
@ -25,7 +25,7 @@ type RunUnshared struct {
RLimits rlimit.RLimits
// Resource limit enforced by tracer
ResLimits specs.ResLimit
ResLimits types.Limit
// Allowed syscall names
SyscallAllowed []string

View File

@ -1,59 +0,0 @@
package specs
// TraceCode is the error type
type TraceCode int
// Different end condtion
const (
TraceCodeNormal TraceCode = iota // 0
TraceCodeInvalid // 1
TraceCodeRE // 2
TraceCodeMLE // 3
TraceCodeTLE // 4
TraceCodeOLE // 5
TraceCodeBan // 6
TraceCodeFatal // 7
)
func (t TraceCode) Error() string {
switch t {
case TraceCodeNormal:
return ""
case TraceCodeRE:
return "runtime error"
case TraceCodeTLE:
return "time limit exceeded"
case TraceCodeMLE:
return "memory limit exceeded"
case TraceCodeOLE:
return "output limit exceeded"
case TraceCodeBan:
return "syscall banned"
case TraceCodeFatal:
return "handle failed"
default:
return "invalid"
}
}
// TraceStat is the time usages in ns
type TraceStat struct {
SetUpTime int64
RunningTime int64
}
// TraceResult is the result returned by strat trace
type TraceResult struct {
UserTime uint64 // used user CPU time (in ms)
UserMem uint64 // used user memory (in kb)
ExitCode int // exit code
TraceStatus TraceCode // the final status for the process
TraceStat // collects time for the process
}
// ResLimit represents the resource limit for traced process
type ResLimit struct {
TimeLimit uint64 // user CPU time limit (in ms)
RealTimeLimit uint64 // sig_kill will force the process to exit after this limit (in ms)
MemoryLimit uint64 // user memory limit (in kB)
}

41
types/status.go Normal file
View File

@ -0,0 +1,41 @@
package types
// Status is the result Status
type Status int
// Different end condtion
const (
StatusNormal Status = iota // 0
StatusInvalid // 1
StatusRE // 2
StatusMLE // 3
StatusTLE // 4
StatusOLE // 5
StatusBan // 6
StatusFatal // 7
)
var (
statusString = []string{
"",
"invalid",
"runtime error",
"memory limit exceeded",
"time limit exceeded",
"output limit exceeded",
"syscall banned",
"runner failed",
}
)
func (t Status) String() string {
i := int(t)
if i >= 0 && i < len(statusString) {
return statusString[i]
}
return "invalid"
}
func (t Status) Error() string {
return t.String()
}

24
types/types.go Normal file
View File

@ -0,0 +1,24 @@
package types
import "time"
// Result is the result returned by strat trace
type Result struct {
Status // the final status for the process
ExitStatus int // exit Status
UserTime uint64 // used user CPU time (in ms)
UserMem uint64 // used user memory (in kb)
Stat // collects time usage for the runner
}
// Stat is the time usages in ns
type Stat struct {
SetUpTime time.Duration
RunningTime time.Duration
}
// Limit represents the resource limit for traced process
type Limit struct {
TimeLimit uint64 // user CPU time limit (in ms)
MemoryLimit uint64 // user memory limit (in kB)
}