mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
set pgid on MacOS
This commit is contained in:
parent
b6036eb830
commit
d664e1eae8
@ -4,6 +4,8 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -15,3 +17,45 @@ func printUsage() {
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Status defines uoj/run_program constants
|
||||
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 runner.Status) int {
|
||||
switch s {
|
||||
case runner.StatusNormal:
|
||||
return int(StatusNormal)
|
||||
case runner.StatusInvalid:
|
||||
return int(StatusInvalid)
|
||||
case runner.StatusTimeLimitExceeded:
|
||||
return int(StatusTLE)
|
||||
case runner.StatusMemoryLimitExceeded:
|
||||
return int(StatusMLE)
|
||||
case runner.StatusOutputLimitExceeded:
|
||||
return int(StatusOLE)
|
||||
case runner.StatusDisallowedSyscall:
|
||||
return int(StatusBan)
|
||||
case runner.StatusSignalled, runner.StatusNonzeroExitStatus:
|
||||
return int(StatusRE)
|
||||
default:
|
||||
return int(StatusFatal)
|
||||
}
|
||||
}
|
||||
|
||||
func debug(v ...interface{}) {
|
||||
if showDetails {
|
||||
fmt.Fprintln(os.Stderr, v...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
@ -13,13 +12,15 @@ import (
|
||||
"github.com/criyle/go-sandbox/pkg/forkexec"
|
||||
"github.com/criyle/go-sandbox/pkg/rlimit"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
|
||||
inputFileName, outputFileName, errorFileName, workPath string
|
||||
|
||||
profilePath string
|
||||
profilePath, result string
|
||||
showDetails bool
|
||||
|
||||
args []string
|
||||
)
|
||||
@ -36,6 +37,8 @@ func main() {
|
||||
flag.StringVar(&errorFileName, "err", "", "Set error file name")
|
||||
flag.StringVar(&workPath, "work-path", "", "Set the work path of the program")
|
||||
flag.StringVar(&profilePath, "p", "", "sandbox profile")
|
||||
flag.BoolVar(&showDetails, "show-trace-details", false, "Show trace details")
|
||||
flag.StringVar(&result, "res", "stdout", "Set the file name for output the result")
|
||||
flag.Parse()
|
||||
|
||||
args = flag.Args()
|
||||
@ -53,8 +56,50 @@ func main() {
|
||||
workPath, _ = os.Getwd()
|
||||
}
|
||||
|
||||
ret, err := start()
|
||||
log.Println(ret, err)
|
||||
var (
|
||||
f *os.File
|
||||
err error
|
||||
)
|
||||
if result == "stdout" {
|
||||
f = os.Stdout
|
||||
} else if result == "stderr" {
|
||||
f = os.Stderr
|
||||
} else {
|
||||
f, err = os.Create(result)
|
||||
if err != nil {
|
||||
debug("Failed to open result file:", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
rt, err := start()
|
||||
debug(rt, err)
|
||||
|
||||
if rt == nil {
|
||||
rt = &runner.Result{
|
||||
Status: runner.StatusRunnerError,
|
||||
}
|
||||
}
|
||||
if err == nil && rt.Status != runner.StatusNormal {
|
||||
err = rt.Status
|
||||
}
|
||||
debug("setupTime: ", rt.SetUpTime)
|
||||
debug("runningTime: ", rt.RunningTime)
|
||||
if err != nil {
|
||||
debug(err)
|
||||
c, ok := err.(runner.Status)
|
||||
if !ok {
|
||||
c = runner.StatusRunnerError
|
||||
}
|
||||
// Handle fatal error from trace
|
||||
fmt.Fprintf(f, "%d %d %d %d\n", getStatus(c), int(rt.Time/time.Millisecond), uint64(rt.Memory)>>10, rt.ExitStatus)
|
||||
if c == runner.StatusRunnerError {
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(f, "%d %d %d %d\n", 0, int(rt.Time/time.Millisecond), uint64(rt.Memory)>>10, rt.ExitStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func start() (*runner.Result, error) {
|
||||
@ -94,8 +139,8 @@ func start() (*runner.Result, error) {
|
||||
Stack: stackLimit << 20,
|
||||
}
|
||||
|
||||
log.Println(rlims)
|
||||
log.Println(args)
|
||||
debug(rlims)
|
||||
debug(args)
|
||||
|
||||
r := forkexec.Runner{
|
||||
Args: args,
|
||||
@ -113,13 +158,20 @@ func start() (*runner.Result, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
killAll(pid)
|
||||
collectZombie(pid)
|
||||
}()
|
||||
|
||||
var (
|
||||
wstatus syscall.WaitStatus
|
||||
rusage syscall.Rusage
|
||||
)
|
||||
for {
|
||||
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
|
||||
for err == syscall.EINTR {
|
||||
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
|
||||
if err == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
fTime = time.Now()
|
||||
if err != nil {
|
||||
@ -128,10 +180,17 @@ func start() (*runner.Result, error) {
|
||||
result := runner.Result{
|
||||
Status: runner.StatusNormal,
|
||||
Time: time.Duration(rusage.Utime.Nano()),
|
||||
Memory: runner.Size(rusage.Maxrss),
|
||||
Memory: runner.Size(rusage.Maxrss), // seems MacOS uses bytes instead of kb
|
||||
SetUpTime: mTime.Sub(sTime),
|
||||
RunningTime: fTime.Sub(mTime),
|
||||
}
|
||||
if uint64(result.Time) > timeLimit*1e9 {
|
||||
result.Status = runner.StatusTimeLimitExceeded
|
||||
}
|
||||
if uint64(result.Memory) > memoryLimit<<20 {
|
||||
result.Status = runner.StatusMemoryLimitExceeded
|
||||
}
|
||||
|
||||
switch {
|
||||
case wstatus.Exited():
|
||||
if status := wstatus.ExitStatus(); status != 0 {
|
||||
@ -140,19 +199,34 @@ func start() (*runner.Result, error) {
|
||||
}
|
||||
|
||||
case wstatus.Signaled():
|
||||
result.Status = runner.StatusSignalled
|
||||
result.ExitStatus = int(wstatus.Signal())
|
||||
return &result, nil
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
if uint64(result.Time) > timeLimit*1e9 {
|
||||
sig := wstatus.Signal()
|
||||
switch sig {
|
||||
case unix.SIGXCPU, unix.SIGKILL:
|
||||
result.Status = runner.StatusTimeLimitExceeded
|
||||
case unix.SIGXFSZ:
|
||||
result.Status = runner.StatusOutputLimitExceeded
|
||||
case unix.SIGSYS:
|
||||
result.Status = runner.StatusDisallowedSyscall
|
||||
default:
|
||||
result.Status = runner.StatusSignalled
|
||||
}
|
||||
if uint64(result.Memory) > memoryLimit<<20 {
|
||||
result.Status = runner.StatusMemoryLimitExceeded
|
||||
}
|
||||
|
||||
result.ExitStatus = int(sig)
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// kill all tracee according to pids
|
||||
func killAll(pgid int) {
|
||||
unix.Kill(-pgid, unix.SIGKILL)
|
||||
}
|
||||
|
||||
// collect died child processes
|
||||
func collectZombie(pgid int) {
|
||||
var wstatus unix.WaitStatus
|
||||
for {
|
||||
if _, err := unix.Wait4(-pgid, &wstatus, unix.WNOHANG, nil); err != unix.EINTR && err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,48 +400,6 @@ func start() (*runner.Result, error) {
|
||||
return &rt, nil
|
||||
}
|
||||
|
||||
func debug(v ...interface{}) {
|
||||
if showDetails {
|
||||
fmt.Fprintln(os.Stderr, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Status defines uoj/run_program constants
|
||||
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 runner.Status) int {
|
||||
switch s {
|
||||
case runner.StatusNormal:
|
||||
return int(StatusNormal)
|
||||
case runner.StatusInvalid:
|
||||
return int(StatusInvalid)
|
||||
case runner.StatusTimeLimitExceeded:
|
||||
return int(StatusTLE)
|
||||
case runner.StatusMemoryLimitExceeded:
|
||||
return int(StatusMLE)
|
||||
case runner.StatusOutputLimitExceeded:
|
||||
return int(StatusOLE)
|
||||
case runner.StatusDisallowedSyscall:
|
||||
return int(StatusBan)
|
||||
case runner.StatusSignalled, runner.StatusNonzeroExitStatus:
|
||||
return int(StatusRE)
|
||||
default:
|
||||
return int(StatusFatal)
|
||||
}
|
||||
}
|
||||
|
||||
type credGen struct {
|
||||
cur uint32
|
||||
}
|
||||
|
||||
@ -37,6 +37,12 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, prof
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// Set pg id
|
||||
_, _, err1 = rawSyscall(funcPC(libc_setpgid_trampoline), 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// Pass 1 & pass 2 assigns fds for child process
|
||||
// Pass 1: fd[i] < i => nextfd
|
||||
if pipe < nextfd {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package darwin
|
||||
package forkexec
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@ -1,4 +1,4 @@
|
||||
package darwin
|
||||
package forkexec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -3,12 +3,8 @@ package forkexec
|
||||
import (
|
||||
"syscall"
|
||||
_ "unsafe" // use go:linkname
|
||||
|
||||
"github.com/criyle/go-sandbox/pkg/darwin" // use sandbox_init
|
||||
)
|
||||
|
||||
var _ = darwin.SandboxInit
|
||||
|
||||
//go:linkname funcPC syscall.funcPC
|
||||
func funcPC(f func()) uintptr
|
||||
|
||||
@ -51,11 +47,8 @@ func libc_execve_trampoline()
|
||||
//go:linkname libc_exit_trampoline syscall.libc_exit_trampoline
|
||||
func libc_exit_trampoline()
|
||||
|
||||
//go:linkname libc_sandbox_init_trampoline github.com/criyle/go-sandbox/pkg/darwin.libc_sandbox_init_trampoline
|
||||
func libc_sandbox_init_trampoline()
|
||||
|
||||
//go:linkname libc_sandbox_free_error_trampoline github.com/criyle/go-sandbox/pkg/darwin.libc_sandbox_free_error_trampoline
|
||||
func libc_sandbox_free_error_trampoline()
|
||||
//go:linkname libc_setpgid_trampoline syscall.libc_setpgid_trampoline
|
||||
func libc_setpgid_trampoline()
|
||||
|
||||
//go:linkname fcntl syscall.fcntl
|
||||
func fcntl(fd int, cmd int, arg int) (val int, err error)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package darwin
|
||||
package forkexec
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
@ -31,9 +31,3 @@ func libc_sandbox_free_error_trampoline()
|
||||
|
||||
//go:linkname libc_sandbox_free_error libc_sandbox_free_error
|
||||
//go:cgo_import_dynamic libc_sandbox_free_error sandbox_free_error "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
//go:linkname syscall3 syscall.syscall
|
||||
func syscall3(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
//go:linkname funcPC syscall.funcPC
|
||||
func funcPC(f func()) uintptr
|
||||
@ -1,7 +0,0 @@
|
||||
// Package seccomp provides a generated filter format for seccomp filter
|
||||
package seccomp
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Filter is the BPF seccomp filter value
|
||||
type Filter []syscall.SockFilter
|
||||
@ -1,8 +1,10 @@
|
||||
// Package seccomp provides a generated filter format for seccomp filter
|
||||
package seccomp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
import "syscall"
|
||||
|
||||
// Filter is the BPF seccomp filter value
|
||||
type Filter []syscall.SockFilter
|
||||
|
||||
// SockFprog converts Filter to SockFprog for seccomp syscall
|
||||
func (f Filter) SockFprog() *syscall.SockFprog {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user