adapted to context

This commit is contained in:
criyle 2020-02-12 03:01:37 -05:00
parent 4ee93c1dae
commit bfedda0ba2
14 changed files with 273 additions and 160 deletions

View File

@ -56,12 +56,12 @@ Default file access syscall check:
- Unauthorized Access
- Disallowed Syscall
- Runtime Error
- Signaled
- Signalled
- `SIGXCPU` / `SIGKILL` are treated as TimeLimitExceeded by rlimit or caller kill
- `SIGXFSZ` is treated as OutputLimitExceeded by rlimit
- `SIGSYS` is treaded as Disallowed Syscall by seccomp
- Potential Runtime error are: `SIGSEGV` (segment fault)
- Nonzero Exit Code
- Nonzero Exit Status
- Program Runner Error
### Result Structure
@ -80,6 +80,16 @@ type Result struct {
}
```
### Runner Interface
Configured runner to run the program. `Context` is used to cancel (control time limit exceeded event; should not be nil).
``` go
type Runner interface {
Run(context.Context) <-chan types.Result
}
```
### Pre-forked Container Protocol
1. Pre-fork container daemons to run programs inside

View File

@ -1,6 +1,7 @@
package main
import (
"context"
"flag"
"fmt"
"io/ioutil"
@ -108,7 +109,7 @@ func main() {
rt, err := start()
if rt == nil {
rt = &types.Result{
Status: types.StatusFatal,
Status: types.StatusRunnerError,
}
}
if err == nil && rt.Status != types.StatusNormal {
@ -120,15 +121,15 @@ func main() {
debug(err)
c, ok := err.(types.Status)
if !ok {
c = types.StatusFatal
c = types.StatusRunnerError
}
// Handle fatal error from trace
fmt.Fprintf(f, "%d %d %d %d\n", int(c), rt.UserTime, rt.UserMem, rt.ExitStatus)
if c == types.StatusFatal {
fmt.Fprintf(f, "%d %d %d %d\n", getStatus(c), int(rt.Time/time.Millisecond), uint64(rt.Memory)>>10, rt.ExitStatus)
if c == types.StatusRunnerError {
os.Exit(1)
}
} else {
fmt.Fprintf(f, "%d %d %d %d\n", 0, rt.UserTime, rt.UserMem, rt.ExitStatus)
fmt.Fprintf(f, "%d %d %d %d\n", 0, int(rt.Time/time.Millisecond), uint64(rt.Memory)>>10, rt.ExitStatus)
}
}
@ -137,8 +138,8 @@ type daemonRunner struct {
*daemon.ExecveParam
}
func (r *daemonRunner) Start(done <-chan struct{}) (<-chan types.Result, error) {
return r.Master.Execve(done, r.ExecveParam)
func (r *daemonRunner) Run(c context.Context) <-chan types.Result {
return r.Master.Execve(c, r.ExecveParam)
}
func start() (*types.Result, error) {
@ -223,6 +224,11 @@ func start() (*types.Result, error) {
actionDefault = seccomp.ActionTrace.WithReturnCode(seccomp.MsgDisallow)
}
limit := types.Limit{
TimeLimit: time.Duration(timeLimit) * time.Second,
MemoryLimit: types.Size(memoryLimit << 20),
}
if runt == "daemon" {
root, err := ioutil.TempDir("", "dm")
if err != nil {
@ -279,10 +285,7 @@ func start() (*types.Result, error) {
WorkDir: "/w",
Files: fds,
RLimits: rlims.PrepareRLimit(),
Limit: types.Limit{
TimeLimit: timeLimit * 1e3,
MemoryLimit: memoryLimit << 10,
},
Limit: limit,
Seccomp: filter,
Root: root,
Mounts: mounts,
@ -307,10 +310,7 @@ func start() (*types.Result, error) {
ExecFile: execFile,
WorkDir: workPath,
RLimits: rlims.PrepareRLimit(),
Limit: types.Limit{
TimeLimit: timeLimit * 1e3,
MemoryLimit: memoryLimit << 10,
},
Limit: limit,
Files: fds,
Seccomp: filter,
ShowDetails: showDetails,
@ -328,22 +328,17 @@ func start() (*types.Result, error) {
// Run tracer
sTime := time.Now()
done := make(chan struct{})
s, err := runner.Start(done)
c, cancel := context.WithTimeout(context.Background(), time.Duration(int64(realTimeLimit)*int64(time.Second)))
defer cancel()
s := runner.Run(c)
rTime := time.Now()
if err != nil {
return nil, fmt.Errorf("failed to execve: %v", err)
}
tC := time.After(time.Duration(int64(realTimeLimit) * int64(time.Second)))
select {
case <-sig:
close(done)
rt = <-s
rt.Status = types.StatusFatal
case <-tC:
close(done)
cancel()
rt = <-s
rt.Status = types.StatusRunnerError
case rt = <-s:
}
@ -366,8 +361,9 @@ func start() (*types.Result, error) {
return nil, fmt.Errorf("cgroup memory: %v", err)
}
debug("cgroup: cpu: ", cpu, " memory: ", memory)
rt.UserTime = cpu / uint64(time.Millisecond)
rt.UserMem = memory >> 10
rt.Time = time.Duration(cpu)
rt.Memory = types.Size(memory)
debug("cgroup:", rt)
}
return &rt, nil
}
@ -377,3 +373,38 @@ func debug(v ...interface{}) {
fmt.Fprintln(os.Stderr, v...)
}
}
type Status int
// UOJ run_program constants
const (
StatusNormal Status = iota // 0
StatusInvalid // 1
StatusRE // 2
StatusMLE // 3
StatusTLE // 4
StatusOLE // 5
StatusBan // 6
StatusFatal // 7
)
func getStatus(s types.Status) int {
switch s {
case types.StatusNormal:
return int(StatusNormal)
case types.StatusInvalid:
return int(StatusInvalid)
case types.StatusTimeLimitExceeded:
return int(StatusTLE)
case types.StatusMemoryLimitExceeded:
return int(StatusMLE)
case types.StatusOutputLimitExceeded:
return int(StatusOLE)
case types.StatusDisallowedSyscall:
return int(StatusBan)
case types.StatusSignalled, types.StatusNonzeroExitStatus:
return int(StatusRE)
default:
return int(StatusFatal)
}
}

View File

@ -3,6 +3,7 @@ package daemon
import (
"fmt"
"syscall"
"time"
"github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/unixsocket"
@ -114,9 +115,9 @@ func (c *containerServer) handleExecve(cmd *ExecCmd, msg *unixsocket.Msg) error
if err != nil {
c.sendErrorReply("execve: wait4 %v", err)
} else {
var status types.Status
userTime := uint64(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms
userMem := uint64(rusage.Maxrss) // kb
status := types.StatusNormal
userTime := time.Duration(rusage.Utime.Nano()) // ns
userMem := types.Size(rusage.Maxrss << 10) // bytes
switch {
case wstatus.Exited():
exitStatus := wstatus.ExitStatus()
@ -124,8 +125,8 @@ func (c *containerServer) handleExecve(cmd *ExecCmd, msg *unixsocket.Msg) error
ExecReply: &ExecReply{
Status: status,
ExitStatus: exitStatus,
UserTime: userTime,
UserMem: userMem,
Time: userTime,
Memory: userMem,
},
}, nil)
@ -133,19 +134,20 @@ func (c *containerServer) handleExecve(cmd *ExecCmd, msg *unixsocket.Msg) error
switch wstatus.Signal() {
// kill signal treats as TLE
case syscall.SIGXCPU, syscall.SIGKILL:
status = types.StatusTLE
status = types.StatusTimeLimitExceeded
case syscall.SIGXFSZ:
status = types.StatusOLE
status = types.StatusOutputLimitExceeded
case syscall.SIGSYS:
status = types.StatusBan
status = types.StatusDisallowedSyscall
default:
status = types.StatusRE
status = types.StatusSignalled
}
c.sendReply(&Reply{
ExecReply: &ExecReply{
ExitStatus: int(wstatus.Signal()),
Status: status,
UserTime: userTime,
UserMem: userMem,
Time: userTime,
Memory: userMem,
},
}, nil)

View File

@ -36,6 +36,7 @@ Any socket related error will cause the daemon exit (with all process inside con
import (
"os"
"syscall"
"time"
"github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/types"
@ -92,8 +93,8 @@ type ErrorReply struct {
type ExecReply struct {
ExitStatus int // waitpid exit status
Status types.Status // return status
UserTime uint64 // waitpid user CPU (ms)
UserMem uint64 // waitpid user memory (kb)
Time time.Duration // waitpid user CPU (ns)
Memory types.Size // waitpid user memory (byte)
}
func (e *ErrorReply) Error() string {

View File

@ -1,6 +1,7 @@
package daemon
import (
"context"
"fmt"
"time"
@ -26,12 +27,23 @@ type ExecveParam struct {
}
// Execve runs process inside container
// accepts done for cancelation
func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.Result, error) {
// accepts context for cancelation
func (m *Master) Execve(c context.Context, param *ExecveParam) <-chan types.Result {
m.mu.Lock()
sTime := time.Now()
// make sure goroutine not leaked (blocked) even if result is not consumed
result := make(chan types.Result, 1)
errResult := func(f string, v ...interface{}) <-chan types.Result {
result <- types.Result{
Status: types.StatusRunnerError,
Error: fmt.Sprintf(f, v...),
}
return result
}
// if execve with fd, put fd at the first parameter
var files []int
if param.ExecFile > 0 {
@ -53,20 +65,20 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
}
if err := m.sendCmd(&cmd, msg); err != nil {
m.mu.Unlock()
return nil, fmt.Errorf("execve: sendCmd %v", err)
return errResult("execve: sendCmd %v", err)
}
// sync function
reply, msg, err := m.recvReply()
if err != nil {
m.mu.Unlock()
return nil, fmt.Errorf("execve: recvReply %v", err)
return errResult("execve: recvReply %v", err)
}
// if sync function did not involved
if reply.Error != nil || msg == nil || msg.Cred == nil {
// tell kill function to exit and sync
m.execveSyncKill()
m.mu.Unlock()
return nil, fmt.Errorf("execve: no pid received or error %v", reply.Error)
return errResult("execve: no pid received or error %v", reply.Error)
}
if param.SyncFunc != nil {
if err := param.SyncFunc(int(msg.Cred.Pid)); err != nil {
@ -75,19 +87,17 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
// tell kill function to exit and sync
m.execveSyncKill()
m.mu.Unlock()
return nil, fmt.Errorf("execve: syncfunc failed %v", err)
return errResult("execve: syncfunc failed %v", err)
}
}
// send to syncFunc ack ok
if err := m.sendCmd(&Cmd{Cmd: cmdOk}, nil); err != nil {
m.mu.Unlock()
return nil, fmt.Errorf("execve: ack failed %v", err)
return errResult("execve: ack failed %v", err)
}
mTime := time.Now()
// make sure goroutine not leaked (blocked) even if result is not consumed
result := make(chan types.Result, 1)
waitDone := make(chan struct{})
// Wait
@ -102,21 +112,21 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
// handle potential error
if err != nil {
result <- types.Result{
Status: types.StatusFatal,
Status: types.StatusRunnerError,
Error: err.Error(),
}
return
}
if reply2.Error != nil {
result <- types.Result{
Status: types.StatusFatal,
Status: types.StatusRunnerError,
Error: reply2.Error.Error(),
}
return
}
if reply2.ExecReply == nil {
result <- types.Result{
Status: types.StatusFatal,
Status: types.StatusRunnerError,
Error: "execve: no reply received",
}
return
@ -125,8 +135,8 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
result <- types.Result{
Status: reply2.ExecReply.Status,
ExitStatus: reply2.ExecReply.ExitStatus,
UserTime: reply2.ExecReply.UserTime,
UserMem: reply2.ExecReply.UserMem,
Time: reply2.ExecReply.Time,
Memory: reply2.ExecReply.Memory,
SetUpTime: mTime.Sub(sTime),
RunningTime: time.Since(mTime),
}
@ -135,13 +145,13 @@ func (m *Master) Execve(done <-chan struct{}, param *ExecveParam) (<-chan types.
// Kill (if wait is done, a kill message need to be send to collect zombies)
go func() {
select {
case <-done:
case <-c.Done():
case <-waitDone:
}
m.sendCmd(&Cmd{Cmd: cmdKill}, nil)
}()
return result, nil
return result
}
// execveSyncKill will send kill and recv reply

View File

@ -55,7 +55,7 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
t.Handler.Debug("tracer started: ", pgid, err)
if err != nil {
t.Handler.Debug("start tracee failed: ", err)
result.Status = types.StatusRE
result.Status = types.StatusRunnerError
return result, err
}
@ -77,11 +77,11 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
// also ensure processes was well terminated
defer func() {
if tle {
err = types.StatusTLE
err = types.StatusTimeLimitExceeded
}
if err2 := recover(); err2 != nil {
t.Handler.Debug(err2)
err = types.StatusFatal
err = types.StatusRunnerError
}
// kill all tracee upon return
killAll(pgid)
@ -101,27 +101,27 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
}
if err != nil {
t.Handler.Debug("wait4 failed: ", err)
return result, types.StatusFatal
return result, types.StatusRunnerError
}
t.Handler.Debug("------ ", pid, " ------")
status := types.StatusNormal
if pid == pgid {
// update resource usage and check against limits
userTime := uint64(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms
userMem := uint64(rusage.Maxrss) // kb
userTime := time.Duration(rusage.Utime.Nano()) // ns
userMem := types.Size(rusage.Maxrss << 10) // bytes
// check tle / mle
if userTime > t.Limit.TimeLimit {
status = types.StatusTLE
status = types.StatusTimeLimitExceeded
}
if userMem > t.Limit.MemoryLimit {
status = types.StatusMLE
status = types.StatusMemoryLimitExceeded
}
result = types.Result{
Status: status,
UserTime: userTime,
UserMem: userMem,
Time: userTime,
Memory: userMem,
}
if status != types.StatusNormal {
return result, status
@ -138,8 +138,8 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
result.ExitStatus = wstatus.ExitStatus()
return result, nil
}
result.Status = types.StatusFatal
return result, types.StatusFatal
result.Status = types.StatusRunnerError
return result, types.StatusRunnerError
}
case wstatus.Signaled():
@ -149,15 +149,16 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
delete(traced, pid)
switch sig {
case unix.SIGXCPU, unix.SIGKILL:
status = types.StatusTLE
status = types.StatusTimeLimitExceeded
case unix.SIGXFSZ:
status = types.StatusOLE
status = types.StatusOutputLimitExceeded
case unix.SIGSYS:
status = types.StatusBan
status = types.StatusDisallowedSyscall
default:
status = types.StatusRE
status = types.StatusSignalled
}
result.Status = status
result.ExitStatus = int(sig)
return result, status
}
unix.PtraceCont(pid, int(sig))
@ -170,7 +171,7 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
// Ptrace set option valid if the tracee is stopped
err = setPtraceOption(pid)
if err != nil {
result.Status = types.StatusFatal
result.Status = types.StatusRunnerError
return result, err
}
}
@ -183,7 +184,7 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
// give the customized handle for syscall
err := t.handleTrap(pid)
if err != nil {
result.Status = types.StatusBan
result.Status = types.StatusDisallowedSyscall
return result, err
}
} else {
@ -212,9 +213,9 @@ func (t *Tracer) TraceRun(done <-chan struct{}, start chan<- struct{}) (result t
// check if cpu rlimit hit
switch stopSig {
case unix.SIGXCPU:
status = types.StatusTLE
status = types.StatusTimeLimitExceeded
case unix.SIGXFSZ:
status = types.StatusOLE
status = types.StatusOutputLimitExceeded
}
if status != types.StatusNormal {
result.Status = status
@ -266,7 +267,7 @@ func (t *Tracer) handleTrap(pid int) error {
return ctx.skipSyscall()
case TraceKill:
return types.StatusBan
return types.StatusDisallowedSyscall
}
}

View File

@ -127,7 +127,7 @@ func (h *tracerHandler) GetSyscallName(ctx *ptracer.Context) (string, error) {
func (h *tracerHandler) HandlerDisallow(name string) error {
if !h.Unsafe {
return types.StatusBan
return types.StatusDisallowedSyscall
}
return nil
}

View File

@ -1,13 +1,15 @@
package ptrace
import (
"context"
"github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/ptracer"
"github.com/criyle/go-sandbox/types"
)
// Start starts the tracing process
func (r *Runner) Start(done <-chan struct{}) (<-chan types.Result, error) {
// Run starts the tracing process
func (r *Runner) Run(c context.Context) <-chan types.Result {
ch := &forkexec.Runner{
Args: r.Args,
Env: r.Env,
@ -31,5 +33,14 @@ func (r *Runner) Start(done <-chan struct{}) (<-chan types.Result, error) {
Runner: ch,
Limit: r.Limit,
}
return tracer.Trace(done)
rt, err := tracer.Trace(c.Done())
if err != nil {
ch := make(chan types.Result, 1)
ch <- types.Result{
Status: types.StatusRunnerError,
Error: err.Error(),
}
rt = ch
}
return rt
}

View File

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

View File

@ -1,6 +1,7 @@
package unshare
import (
"context"
"fmt"
"os"
"time"
@ -16,9 +17,8 @@ const (
UnshareFlags = unix.CLONE_NEWNS | unix.CLONE_NEWPID | unix.CLONE_NEWUSER | unix.CLONE_NEWUTS | unix.CLONE_NEWCGROUP
)
// Start starts the unshared process
func (r *Runner) Start(done <-chan struct{}) (<-chan types.Result, error) {
var err error
// Run starts the unshared process
func (r *Runner) Run(c context.Context) <-chan types.Result {
ch := &forkexec.Runner{
Args: r.Args,
Env: r.Env,
@ -44,8 +44,8 @@ func (r *Runner) Start(done <-chan struct{}) (<-chan types.Result, error) {
// run
go func() {
defer close(finish)
ret, err2 := r.Trace(done, start, ch)
err = err2
ret, err2 := r.Trace(c.Done(), start, ch)
ret.Error = err2.Error()
result <- ret
}()
@ -53,7 +53,7 @@ func (r *Runner) Start(done <-chan struct{}) (<-chan types.Result, error) {
case <-start:
case <-finish:
}
return result, err
return result
}
// Trace tracks child processes
@ -72,7 +72,7 @@ func (r *Runner) Trace(done <-chan struct{}, start chan<- struct{},
pgid, err := runner.Start()
r.println("Starts: ", pgid, err)
if err != nil {
result.Status = types.StatusRE
result.Status = types.StatusRunnerError
return result, err
}
@ -92,7 +92,7 @@ func (r *Runner) Trace(done <-chan struct{}, start chan<- struct{},
defer func() {
if tle {
err = types.StatusTLE
err = types.StatusTimeLimitExceeded
}
// kill all tracee upon return
killAll(pgid)
@ -107,24 +107,24 @@ loop:
_, err := unix.Wait4(pgid, &wstatus, 0, &rusage)
r.println("wait4: ", wstatus)
if err != nil {
return result, types.StatusFatal
return result, types.StatusRunnerError
}
// update resource usage and check against limits
userTime := uint64(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms
userMem := uint64(rusage.Maxrss) // kb
userTime := time.Duration(rusage.Utime.Nano()) // ns
userMem := types.Size(rusage.Maxrss << 10) // bytes // kb
// check tle / mle
if userTime > r.Limit.TimeLimit {
status = types.StatusTLE
status = types.StatusTimeLimitExceeded
}
if userMem > r.Limit.MemoryLimit {
status = types.StatusMLE
status = types.StatusMemoryLimitExceeded
}
result = types.Result{
Status: status,
UserTime: userTime,
UserMem: userMem,
Time: userTime,
Memory: userMem,
}
if status != types.StatusNormal {
return result, status
@ -138,15 +138,16 @@ loop:
sig := wstatus.Signal()
switch sig {
case unix.SIGXCPU, unix.SIGKILL:
status = types.StatusTLE
status = types.StatusTimeLimitExceeded
case unix.SIGXFSZ:
status = types.StatusOLE
status = types.StatusOutputLimitExceeded
case unix.SIGSYS:
status = types.StatusBan
status = types.StatusDisallowedSyscall
default:
status = types.StatusRE
status = types.StatusSignalled
}
result.Status = status
result.ExitStatus = int(sig)
break loop
}
}

16
types/limit.go Normal file
View File

@ -0,0 +1,16 @@
package types
import (
"fmt"
"time"
)
// Limit represents the resource limit for traced process
type Limit struct {
TimeLimit time.Duration // user CPU time limit (in ns)
MemoryLimit Size // user memory limit (in bytes)
}
func (l Limit) String() string {
return fmt.Sprintf("Limit[Time=%v, Memory=%v]", l.TimeLimit, l.MemoryLimit)
}

36
types/result.go Normal file
View File

@ -0,0 +1,36 @@
package types
import (
"fmt"
"time"
)
// Result is the program runner result
type Result struct {
Status // result status
ExitStatus int // exit status (signal number if signalled)
Error string // potential detailed error message (for program runner error)
Time time.Duration // used user CPU time (underlying type int64 in ns)
Memory Size // used user memory (underlying type uint64 in bytes)
// metrics for the program runner
SetUpTime time.Duration
RunningTime time.Duration
}
func (r Result) String() string {
switch r.Status {
case StatusNormal:
return fmt.Sprintf("Result[%v %v][%v %v]", r.Time, r.Memory, r.SetUpTime, r.RunningTime)
case StatusSignalled:
return fmt.Sprintf("Result[Signalled(%d)][%v %v][%v %v]", r.ExitStatus, r.Time, r.Memory, r.SetUpTime, r.RunningTime)
case StatusRunnerError:
return fmt.Sprintf("Result[RunnerFailed(%s)][%v %v][%v %v]", r.Error, r.Time, r.Memory, r.SetUpTime, r.RunningTime)
default:
return fmt.Sprintf("Result[%v(%s)][%v %v][%v %v]", r.Status, r.Error, r.Time, r.Memory, r.SetUpTime, r.RunningTime)
}
}

View File

@ -3,28 +3,39 @@ package types
// Status is the result Status
type Status int
// Different end condition
// Result Status for program runner
const (
StatusNormal Status = iota // 0
StatusInvalid // 1
StatusRE // 2
StatusMLE // 3
StatusTLE // 4
StatusOLE // 5
StatusBan // 6
StatusFatal // 7
StatusInvalid Status = iota // 0 not initialized
// Normal
StatusNormal // 1 normal
// Resource Limit Exceeded
StatusTimeLimitExceeded // 2 tle
StatusMemoryLimitExceeded // 3 mle
StatusOutputLimitExceeded // 4 ole
// Unauthorized Access
StatusDisallowedSyscall // 5 ban
// Runtime Error
StatusSignalled // 6 signalled
StatusNonzeroExitStatus // 7 nonzero exit status
// Programmer Runner Error
StatusRunnerError // 8 runner error
)
var (
statusString = []string{
"Invalid",
"",
"invalid",
"runtime error",
"memory limit exceeded",
"time limit exceeded",
"output limit exceeded",
"syscall banned",
"runner failed",
"Time Limit Exceeded",
"Memory Limit Exceeded",
"Output Limit Exceeded",
"Disallowed Syscall",
"Signalled",
"Nonzero Exit Status",
"Runner Error",
}
)
@ -33,7 +44,7 @@ func (t Status) String() string {
if i >= 0 && i < len(statusString) {
return statusString[i]
}
return "invalid"
return statusString[0]
}
func (t Status) Error() string {

View File

@ -1,21 +0,0 @@
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
Error string // potential detailed error message
UserTime uint64 // used user CPU time (in ms)
UserMem uint64 // used user memory (in kb)
// collects time usage for the runner
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)
}