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"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/criyle/go-sandbox/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -15,3 +17,45 @@ func printUsage() {
|
|||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
os.Exit(2)
|
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"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@ -13,13 +12,15 @@ import (
|
|||||||
"github.com/criyle/go-sandbox/pkg/forkexec"
|
"github.com/criyle/go-sandbox/pkg/forkexec"
|
||||||
"github.com/criyle/go-sandbox/pkg/rlimit"
|
"github.com/criyle/go-sandbox/pkg/rlimit"
|
||||||
"github.com/criyle/go-sandbox/runner"
|
"github.com/criyle/go-sandbox/runner"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
|
timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
|
||||||
inputFileName, outputFileName, errorFileName, workPath string
|
inputFileName, outputFileName, errorFileName, workPath string
|
||||||
|
|
||||||
profilePath string
|
profilePath, result string
|
||||||
|
showDetails bool
|
||||||
|
|
||||||
args []string
|
args []string
|
||||||
)
|
)
|
||||||
@ -36,6 +37,8 @@ func main() {
|
|||||||
flag.StringVar(&errorFileName, "err", "", "Set error file name")
|
flag.StringVar(&errorFileName, "err", "", "Set error file name")
|
||||||
flag.StringVar(&workPath, "work-path", "", "Set the work path of the program")
|
flag.StringVar(&workPath, "work-path", "", "Set the work path of the program")
|
||||||
flag.StringVar(&profilePath, "p", "", "sandbox profile")
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
args = flag.Args()
|
args = flag.Args()
|
||||||
@ -53,8 +56,50 @@ func main() {
|
|||||||
workPath, _ = os.Getwd()
|
workPath, _ = os.Getwd()
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := start()
|
var (
|
||||||
log.Println(ret, err)
|
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) {
|
func start() (*runner.Result, error) {
|
||||||
@ -94,8 +139,8 @@ func start() (*runner.Result, error) {
|
|||||||
Stack: stackLimit << 20,
|
Stack: stackLimit << 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println(rlims)
|
debug(rlims)
|
||||||
log.Println(args)
|
debug(args)
|
||||||
|
|
||||||
r := forkexec.Runner{
|
r := forkexec.Runner{
|
||||||
Args: args,
|
Args: args,
|
||||||
@ -113,46 +158,75 @@ func start() (*runner.Result, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
killAll(pid)
|
||||||
|
collectZombie(pid)
|
||||||
|
}()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
wstatus syscall.WaitStatus
|
wstatus syscall.WaitStatus
|
||||||
rusage syscall.Rusage
|
rusage syscall.Rusage
|
||||||
)
|
)
|
||||||
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
|
for {
|
||||||
for err == syscall.EINTR {
|
|
||||||
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
|
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
|
||||||
}
|
if err == syscall.EINTR {
|
||||||
fTime = time.Now()
|
continue
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
fTime = time.Now()
|
||||||
}
|
if err != nil {
|
||||||
result := runner.Result{
|
return nil, err
|
||||||
Status: runner.StatusNormal,
|
}
|
||||||
Time: time.Duration(rusage.Utime.Nano()),
|
result := runner.Result{
|
||||||
Memory: runner.Size(rusage.Maxrss),
|
Status: runner.StatusNormal,
|
||||||
SetUpTime: mTime.Sub(sTime),
|
Time: time.Duration(rusage.Utime.Nano()),
|
||||||
RunningTime: fTime.Sub(mTime),
|
Memory: runner.Size(rusage.Maxrss), // seems MacOS uses bytes instead of kb
|
||||||
}
|
SetUpTime: mTime.Sub(sTime),
|
||||||
switch {
|
RunningTime: fTime.Sub(mTime),
|
||||||
case wstatus.Exited():
|
}
|
||||||
if status := wstatus.ExitStatus(); status != 0 {
|
if uint64(result.Time) > timeLimit*1e9 {
|
||||||
result.Status = runner.StatusNonzeroExitStatus
|
result.Status = runner.StatusTimeLimitExceeded
|
||||||
return &result, nil
|
}
|
||||||
|
if uint64(result.Memory) > memoryLimit<<20 {
|
||||||
|
result.Status = runner.StatusMemoryLimitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
case wstatus.Signaled():
|
switch {
|
||||||
result.Status = runner.StatusSignalled
|
case wstatus.Exited():
|
||||||
result.ExitStatus = int(wstatus.Signal())
|
if status := wstatus.ExitStatus(); status != 0 {
|
||||||
return &result, nil
|
result.Status = runner.StatusNonzeroExitStatus
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
case wstatus.Signaled():
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if uint64(result.Time) > timeLimit*1e9 {
|
|
||||||
result.Status = runner.StatusTimeLimitExceeded
|
|
||||||
}
|
|
||||||
if uint64(result.Memory) > memoryLimit<<20 {
|
|
||||||
result.Status = runner.StatusMemoryLimitExceeded
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -400,48 +400,6 @@ func start() (*runner.Result, error) {
|
|||||||
return &rt, nil
|
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 {
|
type credGen struct {
|
||||||
cur uint32
|
cur uint32
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,12 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, prof
|
|||||||
goto childerror
|
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 & pass 2 assigns fds for child process
|
||||||
// Pass 1: fd[i] < i => nextfd
|
// Pass 1: fd[i] < i => nextfd
|
||||||
if pipe < nextfd {
|
if pipe < nextfd {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package darwin
|
package forkexec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package darwin
|
package forkexec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -3,12 +3,8 @@ package forkexec
|
|||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
_ "unsafe" // use go:linkname
|
_ "unsafe" // use go:linkname
|
||||||
|
|
||||||
"github.com/criyle/go-sandbox/pkg/darwin" // use sandbox_init
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = darwin.SandboxInit
|
|
||||||
|
|
||||||
//go:linkname funcPC syscall.funcPC
|
//go:linkname funcPC syscall.funcPC
|
||||||
func funcPC(f func()) uintptr
|
func funcPC(f func()) uintptr
|
||||||
|
|
||||||
@ -51,11 +47,8 @@ func libc_execve_trampoline()
|
|||||||
//go:linkname libc_exit_trampoline syscall.libc_exit_trampoline
|
//go:linkname libc_exit_trampoline syscall.libc_exit_trampoline
|
||||||
func libc_exit_trampoline()
|
func libc_exit_trampoline()
|
||||||
|
|
||||||
//go:linkname libc_sandbox_init_trampoline github.com/criyle/go-sandbox/pkg/darwin.libc_sandbox_init_trampoline
|
//go:linkname libc_setpgid_trampoline syscall.libc_setpgid_trampoline
|
||||||
func libc_sandbox_init_trampoline()
|
func libc_setpgid_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 fcntl syscall.fcntl
|
//go:linkname fcntl syscall.fcntl
|
||||||
func fcntl(fd int, cmd int, arg int) (val int, err error)
|
func fcntl(fd int, cmd int, arg int) (val int, err error)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package darwin
|
package forkexec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -31,9 +31,3 @@ func libc_sandbox_free_error_trampoline()
|
|||||||
|
|
||||||
//go:linkname libc_sandbox_free_error libc_sandbox_free_error
|
//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: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
|
package seccomp
|
||||||
|
|
||||||
import (
|
import "syscall"
|
||||||
"syscall"
|
|
||||||
)
|
// Filter is the BPF seccomp filter value
|
||||||
|
type Filter []syscall.SockFilter
|
||||||
|
|
||||||
// SockFprog converts Filter to SockFprog for seccomp syscall
|
// SockFprog converts Filter to SockFprog for seccomp syscall
|
||||||
func (f Filter) SockFprog() *syscall.SockFprog {
|
func (f Filter) SockFprog() *syscall.SockFprog {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user