mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
decoupling rununshared and tracer
This commit is contained in:
parent
3ed7b771c8
commit
b8c180569f
12
README.md
12
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)
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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),
|
||||
})
|
||||
59
types/specs/specs.go
Normal file
59
types/specs/specs.go
Normal 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)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user