diff --git a/README.md b/README.md index d91a211..ebfb4ff 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/cmd/run_program/array_flags.go b/cmd/runprog/array_flags.go similarity index 100% rename from cmd/run_program/array_flags.go rename to cmd/runprog/array_flags.go diff --git a/cmd/run_program/run_util.go b/cmd/runprog/fileutil.go similarity index 100% rename from cmd/run_program/run_util.go rename to cmd/runprog/fileutil.go diff --git a/cmd/run_program/main.go b/cmd/runprog/main.go similarity index 82% rename from cmd/run_program/main.go rename to cmd/runprog/main.go index f2410f7..a6c3962 100644 --- a/cmd/run_program/main.go +++ b/cmd/runprog/main.go @@ -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] \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{ - TimeLimit: timeLimit * 1e3, - RealTimeLimit: realTimeLimit * 1e3, - MemoryLimit: memoryLimit << 10, + ResLimits: types.Limit{ + TimeLimit: timeLimit * 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,16 +266,15 @@ 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{ - TimeLimit: timeLimit * 1e3, - RealTimeLimit: realTimeLimit * 1e3, - MemoryLimit: memoryLimit << 10, + TraceLimit: types.Limit{ + TimeLimit: timeLimit * 1e3, + MemoryLimit: memoryLimit << 10, }, Files: fds, SyscallAllowed: h.SyscallAllow, @@ -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) diff --git a/deamon/container_init.go b/deamon/container_init.go index 45b647d..884c0b6 100644 --- a/deamon/container_init.go +++ b/deamon/container_init.go @@ -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 } diff --git a/deamon/deamon.go b/deamon/deamon.go index 30a05e7..0439bc5 100644 --- a/deamon/deamon.go +++ b/deamon/deamon.go @@ -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 @@ -50,7 +50,7 @@ type Cmd struct { // Reply is the reply message send back to controller type Reply struct { - Error string // empty if no error - ExitStatus int // waitpid exit status - TraceStatus specs.TraceCode // TraceCode + Error string // empty if no error + ExitStatus int // waitpid exit status + Status types.Status // return status } diff --git a/deamon/master_cmd_execve.go b/deamon/master_cmd_execve.go index 46fb9bd..8b758e2 100644 --- a/deamon/master_cmd_execve.go +++ b/deamon/master_cmd_execve.go @@ -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() diff --git a/go.mod b/go.mod index 0f6b04d..5ea712b 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 6be1d5d..fe07177 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-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= diff --git a/pkg/forkexec/runner.go b/pkg/forkexec/runner.go index d98340f..a67ab3c 100644 --- a/pkg/forkexec/runner.go +++ b/pkg/forkexec/runner.go @@ -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 { diff --git a/tracer/context.go b/ptracer/context.go similarity index 98% rename from tracer/context.go rename to ptracer/context.go index 708f85e..aa89c7b 100644 --- a/tracer/context.go +++ b/ptracer/context.go @@ -1,4 +1,4 @@ -package tracer +package ptracer import ( "os" diff --git a/tracer/context_amd64.go b/ptracer/context_amd64.go similarity index 98% rename from tracer/context_amd64.go rename to ptracer/context_amd64.go index f87d330..76682db 100644 --- a/tracer/context_amd64.go +++ b/ptracer/context_amd64.go @@ -1,4 +1,4 @@ -package tracer +package ptracer import ( "syscall" diff --git a/tracer/context_arm.go b/ptracer/context_arm.go similarity index 98% rename from tracer/context_arm.go rename to ptracer/context_arm.go index 5305ce0..651668d 100644 --- a/tracer/context_arm.go +++ b/ptracer/context_arm.go @@ -1,4 +1,4 @@ -package tracer +package ptracer import ( "syscall" diff --git a/tracer/context_arm64.go b/ptracer/context_arm64.go similarity index 98% rename from tracer/context_arm64.go rename to ptracer/context_arm64.go index 72ec1ce..510a127 100644 --- a/tracer/context_arm64.go +++ b/ptracer/context_arm64.go @@ -1,4 +1,4 @@ -package tracer +package ptracer import ( unix "golang.org/x/sys/unix" diff --git a/tracer/context_helper.go b/ptracer/context_helper.go similarity index 99% rename from tracer/context_helper.go rename to ptracer/context_helper.go index 53b3d6f..3fb8056 100644 --- a/tracer/context_helper.go +++ b/ptracer/context_helper.go @@ -1,4 +1,4 @@ -package tracer +package ptracer import ( "syscall" diff --git a/tracer/proc.go b/ptracer/proc.go similarity index 93% rename from tracer/proc.go rename to ptracer/proc.go index 13da50c..cd22a03 100644 --- a/tracer/proc.go +++ b/ptracer/proc.go @@ -1,4 +1,4 @@ -package tracer +package ptracer import ( "fmt" diff --git a/tracer/ptrace.go b/ptracer/ptrace.go similarity index 98% rename from tracer/ptrace.go rename to ptracer/ptrace.go index 79c0bc7..f25c2b6 100644 --- a/tracer/ptrace.go +++ b/ptracer/ptrace.go @@ -1,4 +1,4 @@ -package tracer +package ptracer import ( "syscall" diff --git a/tracer/tracer.go b/ptracer/tracer.go similarity index 97% rename from tracer/tracer.go rename to ptracer/tracer.go index 8efdc25..4ef9d2f 100644 --- a/tracer/tracer.go +++ b/ptracer/tracer.go @@ -1,4 +1,4 @@ -package tracer +package ptracer // TraceAction defines the action returned by TraceHandle type TraceAction int diff --git a/tracer/tracer_track.go b/ptracer/tracer_track.go similarity index 84% rename from tracer/tracer_track.go rename to ptracer/tracer_track.go index 2ad0af1..a81247f 100644 --- a/tracer/tracer_track.go +++ b/ptracer/tracer_track.go @@ -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{ - UserTime: userTime, - UserMem: userMem, - TraceStatus: status, + result = types.Result{ + Status: status, + UserTime: userTime, + UserMem: userMem, } - 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 } } diff --git a/runconfig/config.go b/runner/config/config.go similarity index 98% rename from runconfig/config.go rename to runner/config/config.go index 7338ea5..93422ef 100644 --- a/runconfig/config.go +++ b/runner/config/config.go @@ -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{ diff --git a/runconfig/config_amd64.go b/runner/config/config_amd64.go similarity index 92% rename from runconfig/config_amd64.go rename to runner/config/config_amd64.go index 9583b95..6bd06f4 100644 --- a/runconfig/config_amd64.go +++ b/runner/config/config_amd64.go @@ -1,4 +1,4 @@ -package runconfig +package config // This file includes configs for the run program settings diff --git a/runconfig/config_arm.go b/runner/config/config_arm.go similarity index 96% rename from runconfig/config_arm.go rename to runner/config/config_arm.go index 71ba370..c00caec 100644 --- a/runconfig/config_arm.go +++ b/runner/config/config_arm.go @@ -1,4 +1,4 @@ -package runconfig +package config // This file includes configs for the run program settings diff --git a/runconfig/config_arm64.go b/runner/config/config_arm64.go similarity index 93% rename from runconfig/config_arm64.go rename to runner/config/config_arm64.go index f453a41..3f3432d 100644 --- a/runconfig/config_arm64.go +++ b/runner/config/config_arm64.go @@ -1,4 +1,4 @@ -package runconfig +package config // This file includes configs for the run program settings diff --git a/runconfig/config_loader.go b/runner/config/config_loader.go similarity index 97% rename from runconfig/config_loader.go rename to runner/config/config_loader.go index ff6f3cd..0549bc6 100644 --- a/runconfig/config_loader.go +++ b/runner/config/config_loader.go @@ -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) diff --git a/runconfig/config_type.go b/runner/config/config_type.go similarity index 96% rename from runconfig/config_type.go rename to runner/config/config_type.go index 0c3e92b..aa091f4 100644 --- a/runconfig/config_type.go +++ b/runner/config/config_type.go @@ -1,4 +1,4 @@ -package runconfig +package config // ProgramConfig defines the extra config apply to program type type ProgramConfig struct { diff --git a/runconfig/file_set.go b/runner/config/file_set.go similarity index 99% rename from runconfig/file_set.go rename to runner/config/file_set.go index 663ba98..7e0de91 100644 --- a/runconfig/file_set.go +++ b/runner/config/file_set.go @@ -1,4 +1,4 @@ -package runconfig +package config import ( "path/filepath" diff --git a/runconfig/handle.go b/runner/config/handle.go similarity index 63% rename from runconfig/handle.go rename to runner/config/handle.go index b505cc3..9baace7 100644 --- a/runconfig/handle.go +++ b/runner/config/handle.go @@ -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 diff --git a/runconfig/syscall_counter.go b/runner/config/syscall_counter.go similarity index 97% rename from runconfig/syscall_counter.go rename to runner/config/syscall_counter.go index 5f20cc7..4e1e82d 100644 --- a/runconfig/syscall_counter.go +++ b/runner/config/syscall_counter.go @@ -1,4 +1,4 @@ -package runconfig +package config // SyscallCounter defines a count-down for each each syscall occurs type SyscallCounter map[string]int diff --git a/runprogram/handle.go b/runner/ptrace/handle.go similarity index 79% rename from runprogram/handle.go rename to runner/ptrace/handle.go index e54deb2..81faca6 100644 --- a/runprogram/handle.go +++ b/runner/ptrace/handle.go @@ -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("") 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 { diff --git a/runprogram/runprogram_run.go b/runner/ptrace/run.go similarity index 77% rename from runprogram/runprogram_run.go rename to runner/ptrace/run.go index f47014b..bcf96f7 100644 --- a/runprogram/runprogram_run.go +++ b/runner/ptrace/run.go @@ -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) } diff --git a/runprogram/runprogram.go b/runner/ptrace/runner.go similarity index 89% rename from runprogram/runprogram.go rename to runner/ptrace/runner.go index 4f195d1..58968dd 100644 --- a/runprogram/runprogram.go +++ b/runner/ptrace/runner.go @@ -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 diff --git a/runner/runner.go b/runner/runner.go new file mode 100644 index 0000000..852c67e --- /dev/null +++ b/runner/runner.go @@ -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) +} diff --git a/rununshared/default.go b/runner/unshare/default.go similarity index 98% rename from rununshared/default.go rename to runner/unshare/default.go index 0e827ee..a399436 100644 --- a/rununshared/default.go +++ b/runner/unshare/default.go @@ -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 diff --git a/rununshared/run.go b/runner/unshare/run.go similarity index 75% rename from rununshared/run.go rename to runner/unshare/run.go index 00785a3..7f930d3 100644 --- a/rununshared/run.go +++ b/runner/unshare/run.go @@ -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{ - UserTime: userTime, - UserMem: userMem, - TraceStatus: status, + result = types.Result{ + Status: status, + UserTime: userTime, + UserMem: userMem, } - 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...) } diff --git a/rununshared/rununshared.go b/runner/unshare/runner.go similarity index 81% rename from rununshared/rununshared.go rename to runner/unshare/runner.go index 596d75f..972a332 100644 --- a/rununshared/rununshared.go +++ b/runner/unshare/runner.go @@ -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 diff --git a/types/specs/specs.go b/types/specs/specs.go deleted file mode 100644 index c40f547..0000000 --- a/types/specs/specs.go +++ /dev/null @@ -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) -} diff --git a/types/status.go b/types/status.go new file mode 100644 index 0000000..899ea60 --- /dev/null +++ b/types/status.go @@ -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() +} diff --git a/types/types.go b/types/types.go new file mode 100644 index 0000000..15d61c2 --- /dev/null +++ b/types/types.go @@ -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) +}