diff --git a/README.md b/README.md index 9b94a44..6cde773 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ New Features: 2. More architectures (arm32, arm64, x86) 3. Allow multiple traced programs in different threads 4. Allow pipes as input / output files +5. Use Linux Namespace to isolate file access (elimilate ptrace) Default file access action: @@ -34,13 +35,15 @@ Default file access action: Packages: - seccomp: provides utility function that wrappers libseccomp -- mount: provides utility function that wrappers mount syscall -- rlimit: provides utility function that defines rlimit syscall - forkexec: fork-exec provides mount, unshare, ptrace, seccomp, capset before exec - tracer: ptrace tracer and provides syscall trap filter context - runprogram: wrapper to call forkexec and trecer - rununshared: wrapper to call forkexec and unshared namespaces -- runconfig: defines arch & language specified trace condition +- runconfig: defines arch & language specified trace condition for seccomp and ptrace +- types: general runtime specs + - mount: provides utility function that wrappers mount syscall + - rlimit: provides utility function that defines rlimit syscall + - specs: provides general res / result data structures Executable: @@ -52,5 +55,4 @@ Configuations: TODO: -1. Use Linux Namespace to isolate file access (elimilate ptrace) -2. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4 rusage) +1. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4 rusage) diff --git a/cmd/run_program/main.go b/cmd/run_program/main.go index c54f19a..0680388 100644 --- a/cmd/run_program/main.go +++ b/cmd/run_program/main.go @@ -6,11 +6,11 @@ import ( "io/ioutil" "os" - "github.com/criyle/go-judger/rlimit" "github.com/criyle/go-judger/runconfig" "github.com/criyle/go-judger/runprogram" "github.com/criyle/go-judger/rununshared" - "github.com/criyle/go-judger/tracer" + "github.com/criyle/go-judger/types/rlimit" + "github.com/criyle/go-judger/types/specs" ) const ( @@ -19,7 +19,7 @@ const ( // Runner can be ptraced runner or namespaced runner type Runner interface { - Start() (tracer.TraceResult, error) + Start() (specs.TraceResult, error) } func printUsage() { @@ -122,7 +122,7 @@ func main() { WorkDir: "/w", Files: fds, RLimits: rlims, - ResLimits: tracer.ResLimit{ + ResLimits: specs.ResLimit{ TimeLimit: timeLimit * 1e3, RealTimeLimit: realTimeLimit * 1e3, MemoryLimit: memoryLimit << 10, @@ -143,7 +143,7 @@ func main() { Env: []string{pathEnv}, WorkDir: workPath, RLimits: rlims, - TraceLimit: runprogram.TraceLimit{ + TraceLimit: specs.ResLimit{ TimeLimit: timeLimit * 1e3, RealTimeLimit: realTimeLimit * 1e3, MemoryLimit: memoryLimit << 10, @@ -173,17 +173,16 @@ func main() { // Run tracer rt, err := runner.Start() - println("used process_vm_readv: ", tracer.UseVMReadv) println("results:", rt, err) if err != nil { - c, ok := err.(tracer.TraceCode) + c, ok := err.(specs.TraceCode) if !ok { - c = tracer.TraceCodeFatal + c = specs.TraceCodeFatal } // Handle fatal error from trace fmt.Fprintf(f, "%d %d %d %d\n", int(c), rt.UserTime, rt.UserMem, rt.ExitCode) - if c == tracer.TraceCodeFatal { + if c == specs.TraceCodeFatal { os.Exit(1) } } else { diff --git a/forkexec/fork.go b/forkexec/fork.go index b55e82b..ad28acb 100644 --- a/forkexec/fork.go +++ b/forkexec/fork.go @@ -77,6 +77,7 @@ func (r *Runner) Start() (int, error) { if err1 != 0 || pid != 0 { // restore all signals afterFork() + syscall.ForkLock.Unlock() // synchronize with child for uid / gid map if unshareUser { @@ -89,7 +90,6 @@ func (r *Runner) Start() (int, error) { unix.Close(p[1]) } - syscall.ForkLock.Unlock() if err1 != 0 { return int(pid), syscall.Errno(err1) } diff --git a/forkexec/fork_util.go b/forkexec/fork_util.go index 94ef237..4d1a4f0 100644 --- a/forkexec/fork_util.go +++ b/forkexec/fork_util.go @@ -3,7 +3,7 @@ package forkexec import ( "syscall" - "github.com/criyle/go-judger/mount" + "github.com/criyle/go-judger/types/mount" ) // prepareExec prepares execve parameters diff --git a/forkexec/rlimit.go b/forkexec/rlimit.go deleted file mode 100644 index a97f631..0000000 --- a/forkexec/rlimit.go +++ /dev/null @@ -1,11 +0,0 @@ -package forkexec - -import "syscall" - -// RLimit is the resource limits defined by Linux setrlimit -type RLimit struct { - // Res is the resource type (e.g. syscall.RLIMIT_CPU) - Res int - // Rlim is the limit applied to that resource - Rlim syscall.Rlimit -} diff --git a/forkexec/runner.go b/forkexec/runner.go index e862138..c4b5bed 100644 --- a/forkexec/runner.go +++ b/forkexec/runner.go @@ -5,7 +5,8 @@ package forkexec import ( "syscall" - "github.com/criyle/go-judger/mount" + "github.com/criyle/go-judger/types/mount" + "github.com/criyle/go-judger/types/rlimit" ) // Runner is the RunProgramConfig including the exec path, argv @@ -16,10 +17,10 @@ type Runner struct { Args []string Env []string - // Resource limit set by set rlimit - RLimits []RLimit + // POSIX Resource limit set by set rlimit + RLimits []rlimit.RLimit - // file disriptors for new process, from 0 to len - 1 + // file disriptors map for new process, from 0 to len - 1 Files []uintptr // work path set by chdir(dir) (current working directory for child) diff --git a/forkexec/userns.go b/forkexec/userns.go index 0042e7a..bae9a22 100644 --- a/forkexec/userns.go +++ b/forkexec/userns.go @@ -26,7 +26,7 @@ func writeIDMaps(pid int) error { // writeFile writes file func writeFile(path string, content []byte) error { - fd, err := unix.Open(path, unix.O_RDWR, 0) + fd, err := unix.Open(path, unix.O_RDWR|unix.O_CLOEXEC, 0) if err != nil { return err } diff --git a/runprogram/handle.go b/runprogram/handle.go index 7d53798..4423e9a 100644 --- a/runprogram/handle.go +++ b/runprogram/handle.go @@ -9,6 +9,7 @@ import ( libseccomp "github.com/seccomp/libseccomp-golang" "github.com/criyle/go-judger/tracer" + "github.com/criyle/go-judger/types/specs" ) type tracerHandler struct { @@ -121,7 +122,7 @@ func (h *tracerHandler) GetSyscallName(ctx *tracer.Context) (string, error) { func (h *tracerHandler) HandlerDisallow(name string) error { if !h.Unsafe { - return tracer.TraceCodeBan + return specs.TraceCodeBan } return nil } diff --git a/runprogram/runprogram.go b/runprogram/runprogram.go index 88aaf87..ef651c2 100644 --- a/runprogram/runprogram.go +++ b/runprogram/runprogram.go @@ -3,8 +3,8 @@ package runprogram import ( "syscall" - "github.com/criyle/go-judger/rlimit" - "github.com/criyle/go-judger/tracer" + "github.com/criyle/go-judger/types/rlimit" + "github.com/criyle/go-judger/types/specs" ) // RunProgram defines the spec to run a program safely @@ -22,7 +22,7 @@ type RunProgram struct { RLimits rlimit.RLimits // Res limit enforced by tracer - TraceLimit TraceLimit + TraceLimit specs.ResLimit // Allowed / Traced syscall names // Notice: file access syscalls should be traced @@ -38,14 +38,11 @@ type RunProgram struct { ShowDetails, Unsafe bool } -// TraceLimit defines the limits enforced by tracer -type TraceLimit tracer.ResLimit - // TraceAction defines action against a syscall check type TraceAction int // BanRet defines the return value for a syscall ban acction -var BanRet syscall.Errno = syscall.EACCES +var BanRet = syscall.EACCES // TraceAllow allow the access, trace ban ignores the syscall and set the // return value to BanRet, TraceKill stops the trace action diff --git a/runprogram/runprogram_run.go b/runprogram/runprogram_run.go index 651346c..7aa8d65 100644 --- a/runprogram/runprogram_run.go +++ b/runprogram/runprogram_run.go @@ -6,10 +6,11 @@ import ( "github.com/criyle/go-judger/forkexec" "github.com/criyle/go-judger/seccomp" "github.com/criyle/go-judger/tracer" + "github.com/criyle/go-judger/types/specs" ) // Start starts the tracing process -func (r *RunProgram) Start() (rt tracer.TraceResult, err error) { +func (r *RunProgram) Start() (rt specs.TraceResult, err error) { // build seccomp filter filter, err := buildFilter(r.ShowDetails, r.SyscallAllowed, r.SyscallTraced) if err != nil { @@ -39,7 +40,7 @@ func (r *RunProgram) Start() (rt tracer.TraceResult, err error) { Unsafe: r.Unsafe, Handler: r.Handler, } - return tracer.Trace(th, ch, tracer.ResLimit(r.TraceLimit)) + return tracer.Trace(th, ch, specs.ResLimit(r.TraceLimit)) } // build filter builds the libseccomp filter according to the allow, trace and show details diff --git a/rununshared/default.go b/rununshared/default.go index d2ae547..2a358ba 100644 --- a/rununshared/default.go +++ b/rununshared/default.go @@ -3,7 +3,7 @@ package rununshared import ( "os" - "github.com/criyle/go-judger/mount" + "github.com/criyle/go-judger/types/mount" "golang.org/x/sys/unix" ) diff --git a/rununshared/run.go b/rununshared/run.go index 214ff5e..d532115 100644 --- a/rununshared/run.go +++ b/rununshared/run.go @@ -8,7 +8,7 @@ import ( "github.com/criyle/go-judger/forkexec" "github.com/criyle/go-judger/seccomp" - "github.com/criyle/go-judger/tracer" + "github.com/criyle/go-judger/types/specs" libseccomp "github.com/seccomp/libseccomp-golang" "golang.org/x/sys/unix" ) @@ -20,7 +20,7 @@ const ( ) // Start starts the unshared process -func (r *RunUnshared) Start() (rt tracer.TraceResult, err error) { +func (r *RunUnshared) Start() (rt specs.TraceResult, err error) { filter, err := seccomp.BuildFilter(libseccomp.ActKill, libseccomp.ActTrap, r.SyscallAllowed, []string{}) if err != nil { println(err) @@ -52,12 +52,12 @@ func (r *RunUnshared) Start() (rt tracer.TraceResult, err error) { } // Trace tracks child processes -func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult, err error) { +func (r *RunUnshared) Trace(runner *forkexec.Runner) (result specs.TraceResult, err error) { var ( wstatus unix.WaitStatus // wait4 wait status rusage unix.Rusage // wait4 rusage tle = false - status = tracer.TraceCodeNormal + status = specs.TraceCodeNormal sTime = time.Now().UnixNano() // start time fTime int64 // finish time for setup ) @@ -66,7 +66,7 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult, pgid, err := runner.Start() r.println("Starts: ", pgid, err) if err != nil { - result.TraceStatus = tracer.TraceCodeRE + result.TraceStatus = specs.TraceCodeRE return result, err } // Set real time limit, kill process after it @@ -78,7 +78,7 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult, defer func() { timer.Stop() if tle { - err = tracer.TraceCodeTLE + err = specs.TraceCodeTLE } // kill all tracee upon return killAll(pgid) @@ -94,7 +94,7 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult, pid, err := unix.Wait4(pgid, &wstatus, unix.WALL, &rusage) r.println("wait4: ", wstatus) if err != nil { - return result, tracer.TraceCodeFatal + return result, specs.TraceCodeFatal } // update resource usage and check against limits @@ -103,17 +103,17 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult, // check tle / mle if userTime > r.ResLimits.TimeLimit { - status = tracer.TraceCodeTLE + status = specs.TraceCodeTLE } if userMem > r.ResLimits.MemoryLimit { - status = tracer.TraceCodeMLE + status = specs.TraceCodeMLE } - result = tracer.TraceResult{ + result = specs.TraceResult{ UserTime: userTime, UserMem: userMem, TraceStatus: status, } - if status != tracer.TraceCodeNormal { + if status != specs.TraceCodeNormal { return result, status } @@ -125,13 +125,13 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult, sig := wstatus.Signal() switch sig { case unix.SIGXCPU: - status = tracer.TraceCodeTLE + status = specs.TraceCodeTLE case unix.SIGXFSZ: - status = tracer.TraceCodeOLE + status = specs.TraceCodeOLE case unix.SIGSYS: - status = tracer.TraceCodeBan + status = specs.TraceCodeBan default: - status = tracer.TraceCodeRE + status = specs.TraceCodeRE } result.TraceStatus = status return result, status diff --git a/rununshared/rununshared.go b/rununshared/rununshared.go index 6985841..b4945f3 100644 --- a/rununshared/rununshared.go +++ b/rununshared/rununshared.go @@ -1,9 +1,9 @@ package rununshared import ( - "github.com/criyle/go-judger/mount" - "github.com/criyle/go-judger/rlimit" - "github.com/criyle/go-judger/tracer" + "github.com/criyle/go-judger/types/mount" + "github.com/criyle/go-judger/types/rlimit" + "github.com/criyle/go-judger/types/specs" ) // RunUnshared runs program in unshared namespaces @@ -22,7 +22,7 @@ type RunUnshared struct { RLimits rlimit.RLimits // Resource limit enforced by tracer - ResLimits tracer.ResLimit + ResLimits specs.ResLimit // Allowed syscall names SyscallAllowed []string diff --git a/tracer/tracer.go b/tracer/tracer.go index addc4a1..8efdc25 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -12,70 +12,12 @@ const ( TraceKill ) -// 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 -} - // Runner represents the process runner type Runner interface { // Starts starts the child process and return pid and error if failed Start() (int, error) } -// 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) -} - // Handler defines customized handler for traced syscall type Handler interface { Handle(*Context) TraceAction diff --git a/tracer/tracer_track.go b/tracer/tracer_track.go index d41d86e..7f97d6a 100644 --- a/tracer/tracer_track.go +++ b/tracer/tracer_track.go @@ -4,6 +4,7 @@ import ( "runtime" "time" + "github.com/criyle/go-judger/types/specs" unix "golang.org/x/sys/unix" ) @@ -17,7 +18,7 @@ const ( // Trace traces all child process that created by runner // this function should called only once and in the same thread that // exec tracee -func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, err error) { +func Trace(handler Handler, runner Runner, limits specs.ResLimit) (result specs.TraceResult, err error) { var ( wstatus unix.WaitStatus // wait4 wait status rusage unix.Rusage // wait4 rusage @@ -39,7 +40,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, handler.Debug("tracer started: ", pgid, err) if err != nil { handler.Debug("start tracee failed: ", err) - result.TraceStatus = TraceCodeRE + result.TraceStatus = specs.TraceCodeRE return result, err } @@ -54,11 +55,11 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, defer func() { timer.Stop() if tle { - err = TraceCodeTLE + err = specs.TraceCodeTLE } if err2 := recover(); err2 != nil { handler.Debug(err2) - err = TraceCodeFatal + err = specs.TraceCodeFatal } // kill all tracee upon return killAll(pgid) @@ -78,11 +79,11 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, } if err != nil { handler.Debug("wait4 failed: ", err) - return result, TraceCodeFatal + return result, specs.TraceCodeFatal } handler.Debug("------ ", pid, " ------") - status := TraceCodeNormal + status := specs.TraceCodeNormal if pid == pgid { if initMem == 0 { initMem = uint64(rusage.Maxrss) @@ -94,17 +95,17 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, // check tle / mle if userTime > limits.TimeLimit { - status = TraceCodeTLE + status = specs.TraceCodeTLE } if userMem > limits.MemoryLimit { - status = TraceCodeMLE + status = specs.TraceCodeMLE } - result = TraceResult{ + result = specs.TraceResult{ UserTime: userTime, UserMem: userMem, TraceStatus: status, } - if status != TraceCodeNormal { + if status != specs.TraceCodeNormal { return result, status } } @@ -119,8 +120,8 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, result.ExitCode = wstatus.ExitStatus() return result, nil } - result.TraceStatus = TraceCodeFatal - return result, TraceCodeFatal + result.TraceStatus = specs.TraceCodeFatal + return result, specs.TraceCodeFatal } case wstatus.Signaled(): @@ -130,13 +131,13 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, delete(traced, pid) switch sig { case unix.SIGXCPU: - status = TraceCodeTLE + status = specs.TraceCodeTLE case unix.SIGXFSZ: - status = TraceCodeOLE + status = specs.TraceCodeOLE case unix.SIGSYS: - status = TraceCodeBan + status = specs.TraceCodeBan default: - status = TraceCodeRE + status = specs.TraceCodeRE } result.TraceStatus = status return result, status @@ -151,7 +152,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, // Ptrace set option valid if the tracee is stopped err = setPtraceOption(pid) if err != nil { - result.TraceStatus = TraceCodeFatal + result.TraceStatus = specs.TraceCodeFatal return result, err } } @@ -164,7 +165,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, // give the customized handle for syscall err := handleTrap(handler, pid) if err != nil { - result.TraceStatus = TraceCodeBan + result.TraceStatus = specs.TraceCodeBan return result, err } } else { @@ -193,11 +194,11 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, // check if cpu rlimit hit switch stopSig { case unix.SIGXCPU: - status = TraceCodeTLE + status = specs.TraceCodeTLE case unix.SIGXFSZ: - status = TraceCodeOLE + status = specs.TraceCodeOLE } - if status != TraceCodeNormal { + if status != specs.TraceCodeNormal { result.TraceStatus = status return result, status } @@ -246,7 +247,7 @@ func handleTrap(handler Handler, pid int) error { return ctx.skipSyscall() case TraceKill: - return TraceCodeBan + return specs.TraceCodeBan } } diff --git a/mount/mount.go b/types/mount/mount.go similarity index 100% rename from mount/mount.go rename to types/mount/mount.go diff --git a/rlimit/rlimit.go b/types/rlimit/rlimit.go similarity index 72% rename from rlimit/rlimit.go rename to types/rlimit/rlimit.go index 4c7ed62..ccefca6 100644 --- a/rlimit/rlimit.go +++ b/types/rlimit/rlimit.go @@ -2,8 +2,6 @@ package rlimit import ( "syscall" - - "github.com/criyle/go-judger/forkexec" ) // RLimits defines the rlimit applied by setrlimit syscall to traced process @@ -16,45 +14,53 @@ type RLimits struct { AddressSpace uint64 // in kb } +// RLimit is the resource limits defined by Linux setrlimit +type RLimit struct { + // Res is the resource type (e.g. syscall.RLIMIT_CPU) + Res int + // Rlim is the limit applied to that resource + Rlim syscall.Rlimit +} + func getRlimit(cur, max uint64) syscall.Rlimit { return syscall.Rlimit{Cur: uint64(cur), Max: uint64(max)} } // PrepareRLimit creates rlimit structures for tracee // TimeLimit in s, SizeLimit in byte -func (r *RLimits) PrepareRLimit() []forkexec.RLimit { - var ret []forkexec.RLimit +func (r *RLimits) PrepareRLimit() []RLimit { + var ret []RLimit if r.CPU > 0 { cpuHard := r.CPUHard if cpuHard < r.CPU { cpuHard = r.CPU } - ret = append(ret, forkexec.RLimit{ + ret = append(ret, RLimit{ Res: syscall.RLIMIT_CPU, Rlim: getRlimit(r.CPU, cpuHard), }) } if r.Data > 0 { - ret = append(ret, forkexec.RLimit{ + ret = append(ret, RLimit{ Res: syscall.RLIMIT_DATA, Rlim: getRlimit(r.Data<<10, r.Data<<10), }) } if r.FileSize > 0 { - ret = append(ret, forkexec.RLimit{ + ret = append(ret, RLimit{ Res: syscall.RLIMIT_FSIZE, Rlim: getRlimit(r.FileSize<<10, r.FileSize<<10), }) } if r.Stack > 0 { - ret = append(ret, forkexec.RLimit{ + ret = append(ret, RLimit{ Res: syscall.RLIMIT_STACK, Rlim: getRlimit(r.Stack<<10, r.Stack<<10), }) } if r.AddressSpace > 0 { - ret = append(ret, forkexec.RLimit{ + ret = append(ret, RLimit{ Res: syscall.RLIMIT_AS, Rlim: getRlimit(r.AddressSpace<<10, r.AddressSpace<<10), }) diff --git a/types/specs/specs.go b/types/specs/specs.go new file mode 100644 index 0000000..c40f547 --- /dev/null +++ b/types/specs/specs.go @@ -0,0 +1,59 @@ +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) +}