diff --git a/run_program/config.go b/run_program/config_amd64.go similarity index 100% rename from run_program/config.go rename to run_program/config_amd64.go diff --git a/run_program/config_arm.go b/run_program/config_arm.go new file mode 100644 index 0000000..b9728f5 --- /dev/null +++ b/run_program/config_arm.go @@ -0,0 +1,223 @@ +package main + +// This file includes configs for the run program settings + +var ( + // default read permission files + defaultReadableFiles = []string{ + "/etc/ld.so.nohwcap", + "/etc/ld.so.preload", + "/etc/ld.so.cache", + "/lib/arm-linux-gnueabihf/", // 32-bit + "/usr/lib/arm-linux-gnueabihf/", // 32-bit + "/usr/lib/locale/locale-archive", + "/proc/self/exe", + "/etc/timezone", + "/usr/share/zoneinfo/", + "/dev/random", + "/dev/urandom", + "/proc/meminfo", + "/etc/localtime", + } + + // default write permission files + defaultWritableFiles = []string{"/dev/null"} + + // default allowed safe syscalls + defaultSyscallAllows = []string{ + // file access through fd + "read", + "write", + "readv", + "writev", + "close", + "fstat", + "fstat64", // 32-bit + "lseek", + "_llseek", // 32-bit + "dup", + "dup2", + "dup3", + "ioctl", + "fcntl", + "fcntl64", // 32-bit + + // memory action + "mmap", + "mmap2", // 32-bit + "mprotect", + "munmap", + "brk", + "mremap", + "msync", + "mincore", + "madvise", + + // signal action + "rt_sigaction", + "rt_sigprocmask", + "rt_sigreturn", + "rt_sigpending", + "sigaltstack", + + // get current work dir + "getcwd", + + // process exit + "exit", + "exit_group", + + // others + "arch_prctl", + + "gettimeofday", + "getrlimit", + "getrusage", + "times", + "time", + "clock_gettime", + + "restart_syscall", + + // arch + "uname", + "set_tls", + } + + // default syscalls to trace + defaultSyscallTraces = []string{ + // should be traced + "execve", + + // file open + "open", + "openat", + + // file delete + "unlink", + "unlinkat", + + // soft link + "readlink", + "readlinkat", + + // permission check + "lstat", + "lstat64", // 32-bit + "stat", + "stat64", // 32-bit + "access", + } + + // process related syscall if allowProc enabled + defaultProcSyscalls = []string{"clone", "fork", "vfork", "nanosleep", "execve"} + + // config for different type of program + // workpath and arg0 have additional read / stat permission + runprogramConfig = map[string]ProgramConfig{ + "python2.7": ProgramConfig{ + Syscall: SyscallConfig{ + ExtraAllow: []string{ + "futex", "getdents", "getdents64", "prlimit64", "getpid", "sysinfo", + }, + ExtraCount: map[string]int{ + "set_tid_address": 1, + "set_robust_list": 1, + }, + }, + FileAccess: FileAccessConfig{ + ExtraRead: []string{ + "/usr/bin/python2.7", + "/usr/lib/python2.7/", + "/usr/bin/lib/python2.7/", + "/usr/local/lib/python2.7/", + "/usr/lib/pymodules/python2.7/", + "/usr/bin/Modules/", + "/usr/bin/pybuilddir.txt", + "/usr/lib/locale/", + "./answer.code", + }, + ExtraStat: []string{ + "/usr", "/usr/bin", + }, + }, + RunCommand: []string{"/usr/bin/python2.7", "-E", "-s", "-B"}, + }, + "python3": ProgramConfig{ + Syscall: SyscallConfig{ + ExtraAllow: []string{ + "futex", "getdents", "getdents64", "prlimit64", "getpid", "sysinfo", "getrandom", + }, + ExtraCount: map[string]int{ + "set_tid_address": 1, + "set_robust_list": 1, + }, + }, + FileAccess: FileAccessConfig{ + ExtraRead: []string{ + "/usr/bin/python3", + "/usr/lib/python3/", + "/usr/bin/python3.6", + "/usr/lib/python3.6/", + "/usr/bin/lib/python3.6/", + "/usr/local/lib/python3.6/", + "/usr/bin/pyvenv.cfg", + "/usr/pyvenv.cfg", + "/usr/bin/Modules", + "/usr/bin/pybuilddir.txt", + "/usr/lib/dist-python", + "/usr/lib/locale/", + "./answer.code", + }, + ExtraStat: []string{ + "/usr", "/usr/bin", "/usr/lib", "/usr/lib/python36.zip", + }, + }, + RunCommand: []string{"/usr/bin/python3", "-I", "-B"}, + }, + "compiler": ProgramConfig{ + Syscall: SyscallConfig{ + ExtraAllow: []string{ + "gettid", "set_tid_address", "set_robust_list", "futex", + "getpid", "vfork", "fork", "clone", "execve", "wait4", + "clock_gettime", "clock_getres", + "setrlimit", "pipe", + "getdents64", "getdents", + "umask", "rename", "chmod", "mkdir", + "chdir", "fchdir", + "ftruncate", + "sched_getaffinity", "sched_yield", + "uname", "sysinfo", + "prlimit64", "getrandom", + }, + ExtraBan: []string{"socket", "connect", "geteuid", "getuid"}, + }, + FileAccess: FileAccessConfig{ + ExtraWrite: []string{ + "/tmp/", "./", + }, + ExtraRead: []string{ + "./", + "../runtime/", + "/etc/oracle/java/usagetracker.properties", + "/usr/", + "/lib/", + "/lib64/", + "/bin/", + "/sbin/", + "/sys/devices/system/cpu/", + "/proc/", + "/etc/timezone", + "/etc/fpc-2.6.2.cfg.d/", + "/etc/fpc.cfg", + "/*", + "/", // system_root + }, + ExtraBan: []string{ + "/etc/nsswitch.conf", + "/etc/passwd", + }, + }, + }, + } +) diff --git a/run_program/handle.go b/run_program/handle.go index 9bb446e..d7de230 100644 --- a/run_program/handle.go +++ b/run_program/handle.go @@ -118,9 +118,9 @@ func (h *handler) Handle(ctx *tracer.Context) tracer.TraceAction { case "access": return h.checkStat(ctx, ctx.Arg0()) - case "stat": + case "stat", "stat64": return h.checkStat(ctx, ctx.Arg0()) - case "lstat": + case "lstat", "lstat64": return h.checkStat(ctx, ctx.Arg0()) case "execve": diff --git a/run_program/main.go b/run_program/main.go index 578081b..fc6c707 100644 --- a/run_program/main.go +++ b/run_program/main.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "runtime" - "runtime/debug" secutil "github.com/criyle/go-judger/secutil" tracee "github.com/criyle/go-judger/tracee" @@ -21,7 +20,6 @@ func main() { timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint inputFileName, outputFileName, errorFileName, workPath string ) - debug.SetGCPercent(-1) flag.UintVar(&timeLimit, "tl", 1, "Set time limit (in second)") flag.UintVar(&realTimeLimit, "rtl", 0, "Set real time limit (in second)") @@ -101,11 +99,15 @@ func main() { fds[i] = uintptr(i) } } + + // Rlimit + rlimit := prepareRLimit(timeLimit, realTimeLimit, outputLimit<<20, stackLimit<<20) + // get tracee ch := &tracee.Runner{ Args: args, Env: []string{"PATH=/"}, - RLimits: prepareRLimit(timeLimit, realTimeLimit, outputLimit<<20, stackLimit<<20), + RLimits: rlimit, Files: fds, WorkDir: workPath, BPF: bpf, diff --git a/tracee/tracee_fork.go b/tracee/tracee_fork.go index 9ff57ba..1bf0eb1 100644 --- a/tracee/tracee_fork.go +++ b/tracee/tracee_fork.go @@ -91,7 +91,8 @@ func (r *Runner) Start() (int, error) { // Set limit for _, rlim := range r.RLimits { - _, _, err1 = syscall.RawSyscall(syscall.SYS_SETRLIMIT, uintptr(rlim.Res), uintptr(unsafe.Pointer(&rlim.Rlim)), 0) + // Prlimit instead of setrlimit to avoid 32-bit limitation (linux > 3.2) + _, _, err1 = syscall.RawSyscall6(syscall.SYS_PRLIMIT64, 0, uintptr(rlim.Res), uintptr(unsafe.Pointer(&rlim.Rlim)), 0, 0, 0) if err1 != 0 { goto childerror } diff --git a/tracee/tracee_fork.s b/tracee/tracee_fork.s new file mode 100644 index 0000000..063c650 --- /dev/null +++ b/tracee/tracee_fork.s @@ -0,0 +1 @@ +// to use go:linkname diff --git a/tracer/context_arm.go b/tracer/context_arm.go new file mode 100644 index 0000000..a85e4f1 --- /dev/null +++ b/tracer/context_arm.go @@ -0,0 +1,61 @@ +package tracer + +import ( + "syscall" + + unix "golang.org/x/sys/unix" +) + +// SyscallNo get current syscall no +func (c *Context) SyscallNo() uint { + return uint(c.regs.Uregs[7]) // R7 +} + +// Arg0 gets the arg0 for the current syscall +func (c *Context) Arg0() uint { + return uint(c.regs.Uregs[17]) //Orig_R0 +} + +// Arg1 gets the arg1 for the current syscall +func (c *Context) Arg1() uint { + return uint(c.regs.Uregs[1]) // R1 +} + +// Arg2 gets the arg2 for the current syscall +func (c *Context) Arg2() uint { + return uint(c.regs.Uregs[2]) // R2 +} + +// Arg3 gets the arg3 for the current syscall +func (c *Context) Arg3() uint { + return uint(c.regs.Uregs[3]) // R3 +} + +// Arg4 gets the arg4 for the current syscall +func (c *Context) Arg4() uint { + return uint(c.regs.Uregs[4]) // R4 +} + +// Arg5 gets the arg5 for the current syscall +func (c *Context) Arg5() uint { + return uint(c.regs.Uregs[5]) //R5 +} + +// SetReturnValue set the return value if skip the syscall +func (c *Context) SetReturnValue(retval int) { + c.regs.Uregs[0] = uint32(retval) // R0 +} + +func (c *Context) skipSyscall() error { + c.regs.Uregs[7] = ^uint32(0) //-1 + return syscall.PtraceSetRegs(c.Pid, &c.regs) +} + +func getIovecs(base *byte, l int) []unix.Iovec { + return []unix.Iovec{ + unix.Iovec{ + Base: base, + Len: uint32(l), + }, + } +} diff --git a/tracer/tracer_track.go b/tracer/tracer_track.go index 67468e8..949a9f6 100644 --- a/tracer/tracer_track.go +++ b/tracer/tracer_track.go @@ -83,16 +83,6 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r return results, TraceCodeFatal } println("------ ", pid, " ------") - // Set option if the process is newly forked - if !traced[pid] { - println("set ptrace option") - traced[pid] = true - // Ptrace set option valid if the tracee is stopped - err = setPtraceOption(pid) - if err != nil { - return results, err - } - } // update resource usage and check against limits userTime := uint(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms @@ -155,6 +145,18 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r delete(traced, pid) case wstatus.Stopped(): + // Set option if the process is newly forked + if !traced[pid] { + println("set ptrace option") + traced[pid] = true + // Ptrace set option valid if the tracee is stopped + err = setPtraceOption(pid) + if err != nil { + return results, err + } + } + + // Check stop signal, if trap then check seccomp if stopSig := wstatus.StopSignal(); stopSig == unix.SIGTRAP { switch trapCause := wstatus.TrapCause(); trapCause { case unix.PTRACE_EVENT_SECCOMP: