decoupling rununshared and tracer

This commit is contained in:
criyle 2019-07-20 15:20:42 -07:00
parent 3ed7b771c8
commit b8c180569f
18 changed files with 149 additions and 151 deletions

View File

@ -22,6 +22,7 @@ New Features:
2. More architectures (arm32, arm64, x86) 2. More architectures (arm32, arm64, x86)
3. Allow multiple traced programs in different threads 3. Allow multiple traced programs in different threads
4. Allow pipes as input / output files 4. Allow pipes as input / output files
5. Use Linux Namespace to isolate file access (elimilate ptrace)
Default file access action: Default file access action:
@ -34,13 +35,15 @@ Default file access action:
Packages: Packages:
- seccomp: provides utility function that wrappers libseccomp - 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 - forkexec: fork-exec provides mount, unshare, ptrace, seccomp, capset before exec
- tracer: ptrace tracer and provides syscall trap filter context - tracer: ptrace tracer and provides syscall trap filter context
- runprogram: wrapper to call forkexec and trecer - runprogram: wrapper to call forkexec and trecer
- rununshared: wrapper to call forkexec and unshared namespaces - 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: Executable:
@ -52,5 +55,4 @@ Configuations:
TODO: TODO:
1. Use Linux Namespace to isolate file access (elimilate ptrace) 1. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4 rusage)
2. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4 rusage)

View File

@ -6,11 +6,11 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/criyle/go-judger/rlimit"
"github.com/criyle/go-judger/runconfig" "github.com/criyle/go-judger/runconfig"
"github.com/criyle/go-judger/runprogram" "github.com/criyle/go-judger/runprogram"
"github.com/criyle/go-judger/rununshared" "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 ( const (
@ -19,7 +19,7 @@ const (
// Runner can be ptraced runner or namespaced runner // Runner can be ptraced runner or namespaced runner
type Runner interface { type Runner interface {
Start() (tracer.TraceResult, error) Start() (specs.TraceResult, error)
} }
func printUsage() { func printUsage() {
@ -122,7 +122,7 @@ func main() {
WorkDir: "/w", WorkDir: "/w",
Files: fds, Files: fds,
RLimits: rlims, RLimits: rlims,
ResLimits: tracer.ResLimit{ ResLimits: specs.ResLimit{
TimeLimit: timeLimit * 1e3, TimeLimit: timeLimit * 1e3,
RealTimeLimit: realTimeLimit * 1e3, RealTimeLimit: realTimeLimit * 1e3,
MemoryLimit: memoryLimit << 10, MemoryLimit: memoryLimit << 10,
@ -143,7 +143,7 @@ func main() {
Env: []string{pathEnv}, Env: []string{pathEnv},
WorkDir: workPath, WorkDir: workPath,
RLimits: rlims, RLimits: rlims,
TraceLimit: runprogram.TraceLimit{ TraceLimit: specs.ResLimit{
TimeLimit: timeLimit * 1e3, TimeLimit: timeLimit * 1e3,
RealTimeLimit: realTimeLimit * 1e3, RealTimeLimit: realTimeLimit * 1e3,
MemoryLimit: memoryLimit << 10, MemoryLimit: memoryLimit << 10,
@ -173,17 +173,16 @@ func main() {
// Run tracer // Run tracer
rt, err := runner.Start() rt, err := runner.Start()
println("used process_vm_readv: ", tracer.UseVMReadv)
println("results:", rt, err) println("results:", rt, err)
if err != nil { if err != nil {
c, ok := err.(tracer.TraceCode) c, ok := err.(specs.TraceCode)
if !ok { if !ok {
c = tracer.TraceCodeFatal c = specs.TraceCodeFatal
} }
// Handle fatal error from trace // Handle fatal error from trace
fmt.Fprintf(f, "%d %d %d %d\n", int(c), rt.UserTime, rt.UserMem, rt.ExitCode) 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) os.Exit(1)
} }
} else { } else {

View File

@ -77,6 +77,7 @@ func (r *Runner) Start() (int, error) {
if err1 != 0 || pid != 0 { if err1 != 0 || pid != 0 {
// restore all signals // restore all signals
afterFork() afterFork()
syscall.ForkLock.Unlock()
// synchronize with child for uid / gid map // synchronize with child for uid / gid map
if unshareUser { if unshareUser {
@ -89,7 +90,6 @@ func (r *Runner) Start() (int, error) {
unix.Close(p[1]) unix.Close(p[1])
} }
syscall.ForkLock.Unlock()
if err1 != 0 { if err1 != 0 {
return int(pid), syscall.Errno(err1) return int(pid), syscall.Errno(err1)
} }

View File

@ -3,7 +3,7 @@ package forkexec
import ( import (
"syscall" "syscall"
"github.com/criyle/go-judger/mount" "github.com/criyle/go-judger/types/mount"
) )
// prepareExec prepares execve parameters // prepareExec prepares execve parameters

View File

@ -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
}

View File

@ -5,7 +5,8 @@ package forkexec
import ( import (
"syscall" "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 // Runner is the RunProgramConfig including the exec path, argv
@ -16,10 +17,10 @@ type Runner struct {
Args []string Args []string
Env []string Env []string
// Resource limit set by set rlimit // POSIX Resource limit set by set rlimit
RLimits []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 Files []uintptr
// work path set by chdir(dir) (current working directory for child) // work path set by chdir(dir) (current working directory for child)

View File

@ -26,7 +26,7 @@ func writeIDMaps(pid int) error {
// writeFile writes file // writeFile writes file
func writeFile(path string, content []byte) error { 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 { if err != nil {
return err return err
} }

View File

@ -9,6 +9,7 @@ import (
libseccomp "github.com/seccomp/libseccomp-golang" libseccomp "github.com/seccomp/libseccomp-golang"
"github.com/criyle/go-judger/tracer" "github.com/criyle/go-judger/tracer"
"github.com/criyle/go-judger/types/specs"
) )
type tracerHandler struct { type tracerHandler struct {
@ -121,7 +122,7 @@ func (h *tracerHandler) GetSyscallName(ctx *tracer.Context) (string, error) {
func (h *tracerHandler) HandlerDisallow(name string) error { func (h *tracerHandler) HandlerDisallow(name string) error {
if !h.Unsafe { if !h.Unsafe {
return tracer.TraceCodeBan return specs.TraceCodeBan
} }
return nil return nil
} }

View File

@ -3,8 +3,8 @@ package runprogram
import ( import (
"syscall" "syscall"
"github.com/criyle/go-judger/rlimit" "github.com/criyle/go-judger/types/rlimit"
"github.com/criyle/go-judger/tracer" "github.com/criyle/go-judger/types/specs"
) )
// RunProgram defines the spec to run a program safely // RunProgram defines the spec to run a program safely
@ -22,7 +22,7 @@ type RunProgram struct {
RLimits rlimit.RLimits RLimits rlimit.RLimits
// Res limit enforced by tracer // Res limit enforced by tracer
TraceLimit TraceLimit TraceLimit specs.ResLimit
// Allowed / Traced syscall names // Allowed / Traced syscall names
// Notice: file access syscalls should be traced // Notice: file access syscalls should be traced
@ -38,14 +38,11 @@ type RunProgram struct {
ShowDetails, Unsafe bool ShowDetails, Unsafe bool
} }
// TraceLimit defines the limits enforced by tracer
type TraceLimit tracer.ResLimit
// TraceAction defines action against a syscall check // TraceAction defines action against a syscall check
type TraceAction int type TraceAction int
// BanRet defines the return value for a syscall ban acction // 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 // TraceAllow allow the access, trace ban ignores the syscall and set the
// return value to BanRet, TraceKill stops the trace action // return value to BanRet, TraceKill stops the trace action

View File

@ -6,10 +6,11 @@ import (
"github.com/criyle/go-judger/forkexec" "github.com/criyle/go-judger/forkexec"
"github.com/criyle/go-judger/seccomp" "github.com/criyle/go-judger/seccomp"
"github.com/criyle/go-judger/tracer" "github.com/criyle/go-judger/tracer"
"github.com/criyle/go-judger/types/specs"
) )
// Start starts the tracing process // 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 // build seccomp filter
filter, err := buildFilter(r.ShowDetails, r.SyscallAllowed, r.SyscallTraced) filter, err := buildFilter(r.ShowDetails, r.SyscallAllowed, r.SyscallTraced)
if err != nil { if err != nil {
@ -39,7 +40,7 @@ func (r *RunProgram) Start() (rt tracer.TraceResult, err error) {
Unsafe: r.Unsafe, Unsafe: r.Unsafe,
Handler: r.Handler, 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 // build filter builds the libseccomp filter according to the allow, trace and show details

View File

@ -3,7 +3,7 @@ package rununshared
import ( import (
"os" "os"
"github.com/criyle/go-judger/mount" "github.com/criyle/go-judger/types/mount"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )

View File

@ -8,7 +8,7 @@ import (
"github.com/criyle/go-judger/forkexec" "github.com/criyle/go-judger/forkexec"
"github.com/criyle/go-judger/seccomp" "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" libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -20,7 +20,7 @@ const (
) )
// Start starts the unshared process // 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{}) filter, err := seccomp.BuildFilter(libseccomp.ActKill, libseccomp.ActTrap, r.SyscallAllowed, []string{})
if err != nil { if err != nil {
println(err) println(err)
@ -52,12 +52,12 @@ func (r *RunUnshared) Start() (rt tracer.TraceResult, err error) {
} }
// Trace tracks child processes // 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 ( var (
wstatus unix.WaitStatus // wait4 wait status wstatus unix.WaitStatus // wait4 wait status
rusage unix.Rusage // wait4 rusage rusage unix.Rusage // wait4 rusage
tle = false tle = false
status = tracer.TraceCodeNormal status = specs.TraceCodeNormal
sTime = time.Now().UnixNano() // start time sTime = time.Now().UnixNano() // start time
fTime int64 // finish time for setup fTime int64 // finish time for setup
) )
@ -66,7 +66,7 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult,
pgid, err := runner.Start() pgid, err := runner.Start()
r.println("Starts: ", pgid, err) r.println("Starts: ", pgid, err)
if err != nil { if err != nil {
result.TraceStatus = tracer.TraceCodeRE result.TraceStatus = specs.TraceCodeRE
return result, err return result, err
} }
// Set real time limit, kill process after it // Set real time limit, kill process after it
@ -78,7 +78,7 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult,
defer func() { defer func() {
timer.Stop() timer.Stop()
if tle { if tle {
err = tracer.TraceCodeTLE err = specs.TraceCodeTLE
} }
// kill all tracee upon return // kill all tracee upon return
killAll(pgid) 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) pid, err := unix.Wait4(pgid, &wstatus, unix.WALL, &rusage)
r.println("wait4: ", wstatus) r.println("wait4: ", wstatus)
if err != nil { if err != nil {
return result, tracer.TraceCodeFatal return result, specs.TraceCodeFatal
} }
// update resource usage and check against limits // update resource usage and check against limits
@ -103,17 +103,17 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult,
// check tle / mle // check tle / mle
if userTime > r.ResLimits.TimeLimit { if userTime > r.ResLimits.TimeLimit {
status = tracer.TraceCodeTLE status = specs.TraceCodeTLE
} }
if userMem > r.ResLimits.MemoryLimit { if userMem > r.ResLimits.MemoryLimit {
status = tracer.TraceCodeMLE status = specs.TraceCodeMLE
} }
result = tracer.TraceResult{ result = specs.TraceResult{
UserTime: userTime, UserTime: userTime,
UserMem: userMem, UserMem: userMem,
TraceStatus: status, TraceStatus: status,
} }
if status != tracer.TraceCodeNormal { if status != specs.TraceCodeNormal {
return result, status return result, status
} }
@ -125,13 +125,13 @@ func (r *RunUnshared) Trace(runner *forkexec.Runner) (result tracer.TraceResult,
sig := wstatus.Signal() sig := wstatus.Signal()
switch sig { switch sig {
case unix.SIGXCPU: case unix.SIGXCPU:
status = tracer.TraceCodeTLE status = specs.TraceCodeTLE
case unix.SIGXFSZ: case unix.SIGXFSZ:
status = tracer.TraceCodeOLE status = specs.TraceCodeOLE
case unix.SIGSYS: case unix.SIGSYS:
status = tracer.TraceCodeBan status = specs.TraceCodeBan
default: default:
status = tracer.TraceCodeRE status = specs.TraceCodeRE
} }
result.TraceStatus = status result.TraceStatus = status
return result, status return result, status

View File

@ -1,9 +1,9 @@
package rununshared package rununshared
import ( import (
"github.com/criyle/go-judger/mount" "github.com/criyle/go-judger/types/mount"
"github.com/criyle/go-judger/rlimit" "github.com/criyle/go-judger/types/rlimit"
"github.com/criyle/go-judger/tracer" "github.com/criyle/go-judger/types/specs"
) )
// RunUnshared runs program in unshared namespaces // RunUnshared runs program in unshared namespaces
@ -22,7 +22,7 @@ type RunUnshared struct {
RLimits rlimit.RLimits RLimits rlimit.RLimits
// Resource limit enforced by tracer // Resource limit enforced by tracer
ResLimits tracer.ResLimit ResLimits specs.ResLimit
// Allowed syscall names // Allowed syscall names
SyscallAllowed []string SyscallAllowed []string

View File

@ -12,70 +12,12 @@ const (
TraceKill 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 // Runner represents the process runner
type Runner interface { type Runner interface {
// Starts starts the child process and return pid and error if failed // Starts starts the child process and return pid and error if failed
Start() (int, error) 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 // Handler defines customized handler for traced syscall
type Handler interface { type Handler interface {
Handle(*Context) TraceAction Handle(*Context) TraceAction

View File

@ -4,6 +4,7 @@ import (
"runtime" "runtime"
"time" "time"
"github.com/criyle/go-judger/types/specs"
unix "golang.org/x/sys/unix" unix "golang.org/x/sys/unix"
) )
@ -17,7 +18,7 @@ const (
// Trace traces all child process that created by runner // Trace traces all child process that created by runner
// this function should called only once and in the same thread that // this function should called only once and in the same thread that
// exec tracee // 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 ( var (
wstatus unix.WaitStatus // wait4 wait status wstatus unix.WaitStatus // wait4 wait status
rusage unix.Rusage // wait4 rusage 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) handler.Debug("tracer started: ", pgid, err)
if err != nil { if err != nil {
handler.Debug("start tracee failed: ", err) handler.Debug("start tracee failed: ", err)
result.TraceStatus = TraceCodeRE result.TraceStatus = specs.TraceCodeRE
return result, err return result, err
} }
@ -54,11 +55,11 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
defer func() { defer func() {
timer.Stop() timer.Stop()
if tle { if tle {
err = TraceCodeTLE err = specs.TraceCodeTLE
} }
if err2 := recover(); err2 != nil { if err2 := recover(); err2 != nil {
handler.Debug(err2) handler.Debug(err2)
err = TraceCodeFatal err = specs.TraceCodeFatal
} }
// kill all tracee upon return // kill all tracee upon return
killAll(pgid) killAll(pgid)
@ -78,11 +79,11 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
} }
if err != nil { if err != nil {
handler.Debug("wait4 failed: ", err) handler.Debug("wait4 failed: ", err)
return result, TraceCodeFatal return result, specs.TraceCodeFatal
} }
handler.Debug("------ ", pid, " ------") handler.Debug("------ ", pid, " ------")
status := TraceCodeNormal status := specs.TraceCodeNormal
if pid == pgid { if pid == pgid {
if initMem == 0 { if initMem == 0 {
initMem = uint64(rusage.Maxrss) initMem = uint64(rusage.Maxrss)
@ -94,17 +95,17 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
// check tle / mle // check tle / mle
if userTime > limits.TimeLimit { if userTime > limits.TimeLimit {
status = TraceCodeTLE status = specs.TraceCodeTLE
} }
if userMem > limits.MemoryLimit { if userMem > limits.MemoryLimit {
status = TraceCodeMLE status = specs.TraceCodeMLE
} }
result = TraceResult{ result = specs.TraceResult{
UserTime: userTime, UserTime: userTime,
UserMem: userMem, UserMem: userMem,
TraceStatus: status, TraceStatus: status,
} }
if status != TraceCodeNormal { if status != specs.TraceCodeNormal {
return result, status return result, status
} }
} }
@ -119,8 +120,8 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
result.ExitCode = wstatus.ExitStatus() result.ExitCode = wstatus.ExitStatus()
return result, nil return result, nil
} }
result.TraceStatus = TraceCodeFatal result.TraceStatus = specs.TraceCodeFatal
return result, TraceCodeFatal return result, specs.TraceCodeFatal
} }
case wstatus.Signaled(): case wstatus.Signaled():
@ -130,13 +131,13 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
delete(traced, pid) delete(traced, pid)
switch sig { switch sig {
case unix.SIGXCPU: case unix.SIGXCPU:
status = TraceCodeTLE status = specs.TraceCodeTLE
case unix.SIGXFSZ: case unix.SIGXFSZ:
status = TraceCodeOLE status = specs.TraceCodeOLE
case unix.SIGSYS: case unix.SIGSYS:
status = TraceCodeBan status = specs.TraceCodeBan
default: default:
status = TraceCodeRE status = specs.TraceCodeRE
} }
result.TraceStatus = status result.TraceStatus = status
return result, 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 // Ptrace set option valid if the tracee is stopped
err = setPtraceOption(pid) err = setPtraceOption(pid)
if err != nil { if err != nil {
result.TraceStatus = TraceCodeFatal result.TraceStatus = specs.TraceCodeFatal
return result, err return result, err
} }
} }
@ -164,7 +165,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
// give the customized handle for syscall // give the customized handle for syscall
err := handleTrap(handler, pid) err := handleTrap(handler, pid)
if err != nil { if err != nil {
result.TraceStatus = TraceCodeBan result.TraceStatus = specs.TraceCodeBan
return result, err return result, err
} }
} else { } else {
@ -193,11 +194,11 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
// check if cpu rlimit hit // check if cpu rlimit hit
switch stopSig { switch stopSig {
case unix.SIGXCPU: case unix.SIGXCPU:
status = TraceCodeTLE status = specs.TraceCodeTLE
case unix.SIGXFSZ: case unix.SIGXFSZ:
status = TraceCodeOLE status = specs.TraceCodeOLE
} }
if status != TraceCodeNormal { if status != specs.TraceCodeNormal {
result.TraceStatus = status result.TraceStatus = status
return result, status return result, status
} }
@ -246,7 +247,7 @@ func handleTrap(handler Handler, pid int) error {
return ctx.skipSyscall() return ctx.skipSyscall()
case TraceKill: case TraceKill:
return TraceCodeBan return specs.TraceCodeBan
} }
} }

View File

@ -2,8 +2,6 @@ package rlimit
import ( import (
"syscall" "syscall"
"github.com/criyle/go-judger/forkexec"
) )
// RLimits defines the rlimit applied by setrlimit syscall to traced process // RLimits defines the rlimit applied by setrlimit syscall to traced process
@ -16,45 +14,53 @@ type RLimits struct {
AddressSpace uint64 // in kb 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 { func getRlimit(cur, max uint64) syscall.Rlimit {
return syscall.Rlimit{Cur: uint64(cur), Max: uint64(max)} return syscall.Rlimit{Cur: uint64(cur), Max: uint64(max)}
} }
// PrepareRLimit creates rlimit structures for tracee // PrepareRLimit creates rlimit structures for tracee
// TimeLimit in s, SizeLimit in byte // TimeLimit in s, SizeLimit in byte
func (r *RLimits) PrepareRLimit() []forkexec.RLimit { func (r *RLimits) PrepareRLimit() []RLimit {
var ret []forkexec.RLimit var ret []RLimit
if r.CPU > 0 { if r.CPU > 0 {
cpuHard := r.CPUHard cpuHard := r.CPUHard
if cpuHard < r.CPU { if cpuHard < r.CPU {
cpuHard = r.CPU cpuHard = r.CPU
} }
ret = append(ret, forkexec.RLimit{ ret = append(ret, RLimit{
Res: syscall.RLIMIT_CPU, Res: syscall.RLIMIT_CPU,
Rlim: getRlimit(r.CPU, cpuHard), Rlim: getRlimit(r.CPU, cpuHard),
}) })
} }
if r.Data > 0 { if r.Data > 0 {
ret = append(ret, forkexec.RLimit{ ret = append(ret, RLimit{
Res: syscall.RLIMIT_DATA, Res: syscall.RLIMIT_DATA,
Rlim: getRlimit(r.Data<<10, r.Data<<10), Rlim: getRlimit(r.Data<<10, r.Data<<10),
}) })
} }
if r.FileSize > 0 { if r.FileSize > 0 {
ret = append(ret, forkexec.RLimit{ ret = append(ret, RLimit{
Res: syscall.RLIMIT_FSIZE, Res: syscall.RLIMIT_FSIZE,
Rlim: getRlimit(r.FileSize<<10, r.FileSize<<10), Rlim: getRlimit(r.FileSize<<10, r.FileSize<<10),
}) })
} }
if r.Stack > 0 { if r.Stack > 0 {
ret = append(ret, forkexec.RLimit{ ret = append(ret, RLimit{
Res: syscall.RLIMIT_STACK, Res: syscall.RLIMIT_STACK,
Rlim: getRlimit(r.Stack<<10, r.Stack<<10), Rlim: getRlimit(r.Stack<<10, r.Stack<<10),
}) })
} }
if r.AddressSpace > 0 { if r.AddressSpace > 0 {
ret = append(ret, forkexec.RLimit{ ret = append(ret, RLimit{
Res: syscall.RLIMIT_AS, Res: syscall.RLIMIT_AS,
Rlim: getRlimit(r.AddressSpace<<10, r.AddressSpace<<10), Rlim: getRlimit(r.AddressSpace<<10, r.AddressSpace<<10),
}) })

59
types/specs/specs.go Normal file
View File

@ -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)
}