mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
fix race problem of setpgid and refactor some package
This commit is contained in:
parent
7cfcc5c391
commit
a122ceed5a
61
cmd/run_program/handle.go
Normal file
61
cmd/run_program/handle.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/runprogram"
|
||||||
|
)
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
fs *fileSets
|
||||||
|
sc syscallCounter
|
||||||
|
showDetails bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) print(v ...interface{}) {
|
||||||
|
if h.showDetails {
|
||||||
|
fmt.Fprintln(os.Stderr, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) onDgsFileDetect(name string) runprogram.TraceAction {
|
||||||
|
if h.fs.isSoftBanFile(name) {
|
||||||
|
return runprogram.TraceBan
|
||||||
|
}
|
||||||
|
h.print("Dangerous fileopen: ", name)
|
||||||
|
return runprogram.TraceKill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) CheckRead(fn string) runprogram.TraceAction {
|
||||||
|
if !h.fs.isReadableFile(fn) {
|
||||||
|
return h.onDgsFileDetect(fn)
|
||||||
|
}
|
||||||
|
return runprogram.TraceAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) CheckWrite(fn string) runprogram.TraceAction {
|
||||||
|
if !h.fs.isWritableFile(fn) {
|
||||||
|
return h.onDgsFileDetect(fn)
|
||||||
|
}
|
||||||
|
return runprogram.TraceAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) CheckStat(fn string) runprogram.TraceAction {
|
||||||
|
if !h.fs.isStatableFile(fn) {
|
||||||
|
return h.onDgsFileDetect(fn)
|
||||||
|
}
|
||||||
|
return runprogram.TraceAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) CheckSyscall(syscallName string) runprogram.TraceAction {
|
||||||
|
// if it is traced, then try to count syscall
|
||||||
|
if inside, allow := h.sc.check(syscallName); inside {
|
||||||
|
if allow {
|
||||||
|
return runprogram.TraceAllow
|
||||||
|
}
|
||||||
|
return runprogram.TraceKill
|
||||||
|
}
|
||||||
|
// if it is traced but not counted, it should be soft banned
|
||||||
|
return runprogram.TraceBan
|
||||||
|
}
|
||||||
@ -4,11 +4,9 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
secutil "github.com/criyle/go-judger/secutil"
|
"github.com/criyle/go-judger/runprogram"
|
||||||
tracee "github.com/criyle/go-judger/tracee"
|
"github.com/criyle/go-judger/tracer"
|
||||||
tracer "github.com/criyle/go-judger/tracer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: syscall handle, file access checker
|
// TODO: syscall handle, file access checker
|
||||||
@ -64,24 +62,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ptrace require running at the same OS thread
|
|
||||||
runtime.LockOSThread()
|
|
||||||
defer runtime.UnlockOSThread()
|
|
||||||
|
|
||||||
// build seccomp filter
|
|
||||||
filter, err := buildFilter(showDetails, allow, trace)
|
|
||||||
if err != nil {
|
|
||||||
println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer filter.Release()
|
|
||||||
|
|
||||||
bpf, err := secutil.FilterToBPF(filter)
|
|
||||||
if err != nil {
|
|
||||||
println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// open input / output / err files
|
// open input / output / err files
|
||||||
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
|
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -100,20 +80,28 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rlimit
|
runner := &runprogram.RunProgram{
|
||||||
rlimit := prepareRLimit(timeLimit, realTimeLimit, outputLimit<<20, stackLimit<<20)
|
|
||||||
|
|
||||||
// get tracee
|
|
||||||
ch := &tracee.Runner{
|
|
||||||
Args: args,
|
Args: args,
|
||||||
Env: []string{"PATH=/"},
|
Env: []string{"PATH=/"},
|
||||||
RLimits: rlimit,
|
|
||||||
Files: fds,
|
|
||||||
WorkDir: workPath,
|
WorkDir: workPath,
|
||||||
BPF: bpf,
|
RLimits: runprogram.RLimits{
|
||||||
|
CPU: timeLimit,
|
||||||
|
CPUHard: realTimeLimit,
|
||||||
|
FileSize: outputLimit,
|
||||||
|
Stack: stackLimit,
|
||||||
|
},
|
||||||
|
TraceLimit: runprogram.TraceLimit{
|
||||||
|
TimeLimit: timeLimit * 1e3,
|
||||||
|
RealTimeLimit: realTimeLimit * 1e3,
|
||||||
|
MemoryLimit: memoryLimit << 10,
|
||||||
|
},
|
||||||
|
Files: fds,
|
||||||
|
SyscallAllowed: allow,
|
||||||
|
SyscallTraced: trace,
|
||||||
|
ShowDetails: showDetails,
|
||||||
|
Unsafe: unsafe,
|
||||||
|
Handler: &handler{fs, sc, showDetails},
|
||||||
}
|
}
|
||||||
// get syscall handler
|
|
||||||
h := &handler{fs, sc, showDetails}
|
|
||||||
|
|
||||||
var f *os.File
|
var f *os.File
|
||||||
if result == "stdout" {
|
if result == "stdout" {
|
||||||
@ -129,17 +117,8 @@ func main() {
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
tracer.ShowDetails = showDetails
|
|
||||||
tracer.Unsafe = unsafe
|
|
||||||
limits := tracer.ResLimit{
|
|
||||||
TimeLimit: timeLimit * 1e3,
|
|
||||||
RealTimeLimit: realTimeLimit * 1e3,
|
|
||||||
MemoryLimit: memoryLimit << 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run tracer
|
// Run tracer
|
||||||
rt, err := tracer.Trace(h, ch, limits)
|
rt, err := runner.Start()
|
||||||
|
|
||||||
println("used process_vm_readv: ", tracer.UseVMReadv)
|
println("used process_vm_readv: ", tracer.UseVMReadv)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
40
cmd/run_program/run_util.go
Normal file
40
cmd/run_program/run_util.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// prepareFile opens file for new process
|
||||||
|
func prepareFiles(inputFile, outputFile, errorFile string) ([]*os.File, error) {
|
||||||
|
var err error
|
||||||
|
files := make([]*os.File, 3)
|
||||||
|
if inputFile != "" {
|
||||||
|
files[0], err = os.OpenFile(inputFile, os.O_RDONLY, 0755)
|
||||||
|
if err != nil {
|
||||||
|
goto openerror
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if outputFile != "" {
|
||||||
|
files[1], err = os.OpenFile(outputFile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0755)
|
||||||
|
if err != nil {
|
||||||
|
goto openerror
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errorFile != "" {
|
||||||
|
files[2], err = os.OpenFile(errorFile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0755)
|
||||||
|
if err != nil {
|
||||||
|
goto openerror
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
openerror:
|
||||||
|
closeFiles(files)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeFiles close all file in the list
|
||||||
|
func closeFiles(files []*os.File) {
|
||||||
|
for _, f := range files {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,148 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
tracer "github.com/criyle/go-judger/tracer"
|
|
||||||
libseccomp "github.com/seccomp/libseccomp-golang"
|
|
||||||
)
|
|
||||||
|
|
||||||
type handler struct {
|
|
||||||
fs *fileSets
|
|
||||||
sc syscallCounter
|
|
||||||
showDetails bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func softBanSyscall(ctx *tracer.Context) tracer.TraceAction {
|
|
||||||
ctx.SetReturnValue(-int(syscall.EACCES))
|
|
||||||
return tracer.TraceBan
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFileMode(flags uint) string {
|
|
||||||
switch flags & syscall.O_ACCMODE {
|
|
||||||
case syscall.O_RDONLY:
|
|
||||||
return "r "
|
|
||||||
case syscall.O_WRONLY:
|
|
||||||
return "w "
|
|
||||||
case syscall.O_RDWR:
|
|
||||||
return "wr"
|
|
||||||
default:
|
|
||||||
return "??"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) print(v ...interface{}) {
|
|
||||||
if h.showDetails {
|
|
||||||
fmt.Fprintln(os.Stderr, v...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) onDgsFileDetect(ctx *tracer.Context, name string) tracer.TraceAction {
|
|
||||||
if h.fs.isSoftBanFile(name) {
|
|
||||||
return softBanSyscall(ctx)
|
|
||||||
}
|
|
||||||
h.print("Dangerous fileopen: (killed)", name)
|
|
||||||
return tracer.TraceKill
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) checkOpen(ctx *tracer.Context, addr uint, flags uint) tracer.TraceAction {
|
|
||||||
fn := ctx.GetString(uintptr(addr))
|
|
||||||
isReadOnly := (flags&syscall.O_ACCMODE == syscall.O_RDONLY) &&
|
|
||||||
(flags&syscall.O_CREAT == 0) &&
|
|
||||||
(flags&syscall.O_EXCL == 0) &&
|
|
||||||
(flags&syscall.O_TRUNC == 0)
|
|
||||||
|
|
||||||
h.print("open: ", fn, getFileMode(flags))
|
|
||||||
if isReadOnly {
|
|
||||||
if realPath(fn) != "" && !h.fs.isReadableFile(fn) {
|
|
||||||
return h.onDgsFileDetect(ctx, fn)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if realPath(fn) != "" && !h.fs.isWritableFile(fn) {
|
|
||||||
return h.onDgsFileDetect(ctx, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tracer.TraceAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) checkRead(ctx *tracer.Context, addr uint) tracer.TraceAction {
|
|
||||||
fn := ctx.GetString(uintptr(addr))
|
|
||||||
h.print("check read: ", fn)
|
|
||||||
if !h.fs.isReadableFile(fn) {
|
|
||||||
return h.onDgsFileDetect(ctx, fn)
|
|
||||||
}
|
|
||||||
return tracer.TraceAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) checkWrite(ctx *tracer.Context, addr uint) tracer.TraceAction {
|
|
||||||
fn := ctx.GetString(uintptr(addr))
|
|
||||||
h.print("check write: ", fn)
|
|
||||||
if !h.fs.isWritableFile(fn) {
|
|
||||||
return h.onDgsFileDetect(ctx, fn)
|
|
||||||
}
|
|
||||||
return tracer.TraceAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) checkStat(ctx *tracer.Context, addr uint) tracer.TraceAction {
|
|
||||||
fn := ctx.GetString(uintptr(addr))
|
|
||||||
h.print("check stat: ", fn)
|
|
||||||
if !h.fs.isStatableFile(fn) {
|
|
||||||
return h.onDgsFileDetect(ctx, fn)
|
|
||||||
}
|
|
||||||
return tracer.TraceAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) Handle(ctx *tracer.Context) tracer.TraceAction {
|
|
||||||
syscallNo := ctx.SyscallNo()
|
|
||||||
syscallName, err := libseccomp.ScmpSyscall(syscallNo).GetName()
|
|
||||||
h.print("syscall: ", syscallNo, syscallName, err)
|
|
||||||
|
|
||||||
switch syscallName {
|
|
||||||
case "open":
|
|
||||||
return h.checkOpen(ctx, ctx.Arg0(), ctx.Arg1())
|
|
||||||
case "openat":
|
|
||||||
return h.checkOpen(ctx, ctx.Arg1(), ctx.Arg2())
|
|
||||||
|
|
||||||
case "readlink":
|
|
||||||
return h.checkRead(ctx, ctx.Arg0())
|
|
||||||
case "readlinkat":
|
|
||||||
return h.checkRead(ctx, ctx.Arg1())
|
|
||||||
|
|
||||||
case "unlink":
|
|
||||||
return h.checkWrite(ctx, ctx.Arg0())
|
|
||||||
case "unlinkat":
|
|
||||||
return h.checkWrite(ctx, ctx.Arg1())
|
|
||||||
|
|
||||||
case "access":
|
|
||||||
return h.checkStat(ctx, ctx.Arg0())
|
|
||||||
|
|
||||||
case "stat", "stat64":
|
|
||||||
return h.checkStat(ctx, ctx.Arg0())
|
|
||||||
case "lstat", "lstat64":
|
|
||||||
return h.checkStat(ctx, ctx.Arg0())
|
|
||||||
|
|
||||||
case "execve":
|
|
||||||
return h.checkRead(ctx, ctx.Arg0())
|
|
||||||
|
|
||||||
case "chmod":
|
|
||||||
return h.checkWrite(ctx, ctx.Arg0())
|
|
||||||
case "rename":
|
|
||||||
return h.checkWrite(ctx, ctx.Arg0())
|
|
||||||
default:
|
|
||||||
// if it is traced, then try to count syscall
|
|
||||||
if inside, allow := h.sc.check(syscallName); !allow {
|
|
||||||
return tracer.TraceKill
|
|
||||||
} else if !inside {
|
|
||||||
// if it is traced but not counted, it should be soft banned
|
|
||||||
return softBanSyscall(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tracer.TraceAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) GetSyscallName(ctx *tracer.Context) (string, error) {
|
|
||||||
syscallNo := ctx.SyscallNo()
|
|
||||||
return libseccomp.ScmpSyscall(syscallNo).GetName()
|
|
||||||
}
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
secutil "github.com/criyle/go-judger/secutil"
|
|
||||||
tracee "github.com/criyle/go-judger/tracee"
|
|
||||||
tracer "github.com/criyle/go-judger/tracer"
|
|
||||||
libseccomp "github.com/seccomp/libseccomp-golang"
|
|
||||||
)
|
|
||||||
|
|
||||||
// build filter builds the libseccomp filter according to the allow, trace and show details
|
|
||||||
func buildFilter(showDetails bool, allow, trace []string) (*libseccomp.ScmpFilter, error) {
|
|
||||||
// make filter
|
|
||||||
var defaultAction libseccomp.ScmpAction
|
|
||||||
// if debug, allow all syscalls and output what was blocked
|
|
||||||
if showDetails {
|
|
||||||
defaultAction = libseccomp.ActTrace.SetReturnCode(tracer.MsgDisallow)
|
|
||||||
} else {
|
|
||||||
defaultAction = libseccomp.ActKill
|
|
||||||
}
|
|
||||||
return secutil.BuildFilter(defaultAction, libseccomp.ActTrace.SetReturnCode(tracer.MsgHandle), allow, trace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepareFile opens file for new process
|
|
||||||
func prepareFiles(inputFile, outputFile, errorFile string) ([]*os.File, error) {
|
|
||||||
var err error
|
|
||||||
files := make([]*os.File, 3)
|
|
||||||
if inputFile != "" {
|
|
||||||
files[0], err = os.OpenFile(inputFile, os.O_RDONLY, 0755)
|
|
||||||
if err != nil {
|
|
||||||
goto openerror
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if outputFile != "" {
|
|
||||||
files[1], err = os.OpenFile(outputFile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0755)
|
|
||||||
if err != nil {
|
|
||||||
goto openerror
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if errorFile != "" {
|
|
||||||
files[2], err = os.OpenFile(errorFile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0755)
|
|
||||||
if err != nil {
|
|
||||||
goto openerror
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return files, nil
|
|
||||||
openerror:
|
|
||||||
closeFiles(files)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeFiles close all file in the list
|
|
||||||
func closeFiles(files []*os.File) {
|
|
||||||
for _, f := range files {
|
|
||||||
if f != nil {
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRlimit(cur, max uint) syscall.Rlimit {
|
|
||||||
return syscall.Rlimit{Cur: uint64(cur), Max: uint64(max)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepareRLimit creates rlimit structures for tracee
|
|
||||||
// TimeLimit in s, SizeLimit in byte
|
|
||||||
func prepareRLimit(TimeLimit, RealTimeLimit, OutputLimit, StackLimit uint) []tracee.RLimit {
|
|
||||||
return []tracee.RLimit{
|
|
||||||
// CPU limit
|
|
||||||
{
|
|
||||||
Res: syscall.RLIMIT_CPU,
|
|
||||||
Rlim: getRlimit(TimeLimit, RealTimeLimit),
|
|
||||||
},
|
|
||||||
// File limit
|
|
||||||
{
|
|
||||||
Res: syscall.RLIMIT_FSIZE,
|
|
||||||
Rlim: getRlimit(OutputLimit, OutputLimit),
|
|
||||||
},
|
|
||||||
// Stack limit
|
|
||||||
{
|
|
||||||
Res: syscall.RLIMIT_STACK,
|
|
||||||
Rlim: getRlimit(StackLimit, StackLimit),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
137
runprogram/handle.go
Normal file
137
runprogram/handle.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package runprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/tracer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tracerHandler struct {
|
||||||
|
ShowDetails, Unsafe bool
|
||||||
|
Handler Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) Debug(v ...interface{}) {
|
||||||
|
if h.ShowDetails {
|
||||||
|
fmt.Fprintln(os.Stderr, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) checkOpen(ctx *tracer.Context, addr uint, flags uint) TraceAction {
|
||||||
|
fn := ctx.GetString(uintptr(addr))
|
||||||
|
isReadOnly := (flags&syscall.O_ACCMODE == syscall.O_RDONLY) &&
|
||||||
|
(flags&syscall.O_CREAT == 0) &&
|
||||||
|
(flags&syscall.O_EXCL == 0) &&
|
||||||
|
(flags&syscall.O_TRUNC == 0)
|
||||||
|
|
||||||
|
h.Debug("open: ", fn, getFileMode(flags))
|
||||||
|
if isReadOnly {
|
||||||
|
return h.Handler.CheckRead(fn)
|
||||||
|
}
|
||||||
|
return h.Handler.CheckWrite(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) checkRead(ctx *tracer.Context, addr uint) TraceAction {
|
||||||
|
fn := ctx.GetString(uintptr(addr))
|
||||||
|
h.Debug("check read: ", fn)
|
||||||
|
return h.Handler.CheckRead(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) checkWrite(ctx *tracer.Context, addr uint) TraceAction {
|
||||||
|
fn := ctx.GetString(uintptr(addr))
|
||||||
|
h.Debug("check write: ", fn)
|
||||||
|
return h.Handler.CheckWrite(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) checkStat(ctx *tracer.Context, addr uint) TraceAction {
|
||||||
|
fn := ctx.GetString(uintptr(addr))
|
||||||
|
h.Debug("check stat: ", fn)
|
||||||
|
return h.Handler.CheckStat(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) Handle(ctx *tracer.Context) tracer.TraceAction {
|
||||||
|
var (
|
||||||
|
action TraceAction
|
||||||
|
syscallNo = ctx.SyscallNo()
|
||||||
|
syscallName, err = libseccomp.ScmpSyscall(syscallNo).GetName()
|
||||||
|
)
|
||||||
|
h.Debug("syscall: ", syscallNo, syscallName, err)
|
||||||
|
|
||||||
|
switch syscallName {
|
||||||
|
case "open":
|
||||||
|
action = h.checkOpen(ctx, ctx.Arg0(), ctx.Arg1())
|
||||||
|
case "openat":
|
||||||
|
action = h.checkOpen(ctx, ctx.Arg1(), ctx.Arg2())
|
||||||
|
|
||||||
|
case "readlink":
|
||||||
|
action = h.checkRead(ctx, ctx.Arg0())
|
||||||
|
case "readlinkat":
|
||||||
|
action = h.checkRead(ctx, ctx.Arg1())
|
||||||
|
|
||||||
|
case "unlink":
|
||||||
|
action = h.checkWrite(ctx, ctx.Arg0())
|
||||||
|
case "unlinkat":
|
||||||
|
action = h.checkWrite(ctx, ctx.Arg1())
|
||||||
|
|
||||||
|
case "access":
|
||||||
|
action = h.checkStat(ctx, ctx.Arg0())
|
||||||
|
|
||||||
|
case "stat", "stat64":
|
||||||
|
action = h.checkStat(ctx, ctx.Arg0())
|
||||||
|
case "lstat", "lstat64":
|
||||||
|
action = h.checkStat(ctx, ctx.Arg0())
|
||||||
|
|
||||||
|
case "execve":
|
||||||
|
action = h.checkRead(ctx, ctx.Arg0())
|
||||||
|
|
||||||
|
case "chmod":
|
||||||
|
action = h.checkWrite(ctx, ctx.Arg0())
|
||||||
|
case "rename":
|
||||||
|
action = h.checkWrite(ctx, ctx.Arg0())
|
||||||
|
default:
|
||||||
|
action = h.Handler.CheckSyscall(syscallName)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case TraceAllow:
|
||||||
|
return tracer.TraceAllow
|
||||||
|
case TraceBan:
|
||||||
|
return softBanSyscall(ctx)
|
||||||
|
default:
|
||||||
|
return tracer.TraceKill
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) GetSyscallName(ctx *tracer.Context) (string, error) {
|
||||||
|
syscallNo := ctx.SyscallNo()
|
||||||
|
return libseccomp.ScmpSyscall(syscallNo).GetName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *tracerHandler) HandlerDisallow(name string) error {
|
||||||
|
if !h.Unsafe {
|
||||||
|
return tracer.TraceCodeBan
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func softBanSyscall(ctx *tracer.Context) tracer.TraceAction {
|
||||||
|
ctx.SetReturnValue(-int(BanRet))
|
||||||
|
return tracer.TraceBan
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileMode(flags uint) string {
|
||||||
|
switch flags & syscall.O_ACCMODE {
|
||||||
|
case syscall.O_RDONLY:
|
||||||
|
return "r "
|
||||||
|
case syscall.O_WRONLY:
|
||||||
|
return "w "
|
||||||
|
case syscall.O_RDWR:
|
||||||
|
return "wr"
|
||||||
|
default:
|
||||||
|
return "??"
|
||||||
|
}
|
||||||
|
}
|
||||||
63
runprogram/rlimit.go
Normal file
63
runprogram/rlimit.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package runprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/tracee"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RLimits defines the rlimit applied by setrlimit syscall to traced process
|
||||||
|
type RLimits struct {
|
||||||
|
CPU uint // in s
|
||||||
|
CPUHard uint // in s
|
||||||
|
Data uint // in kb
|
||||||
|
FileSize uint // in kb
|
||||||
|
Stack uint // in kb
|
||||||
|
AddressSpace uint // in kb
|
||||||
|
}
|
||||||
|
|
||||||
|
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() []tracee.RLimit {
|
||||||
|
var ret []tracee.RLimit
|
||||||
|
if r.CPU > 0 {
|
||||||
|
cpuHard := r.CPUHard
|
||||||
|
if cpuHard < r.CPU {
|
||||||
|
cpuHard = r.CPU
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, tracee.RLimit{
|
||||||
|
Res: syscall.RLIMIT_CPU,
|
||||||
|
Rlim: getRlimit(uint64(r.CPU), uint64(cpuHard)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.Data > 0 {
|
||||||
|
ret = append(ret, tracee.RLimit{
|
||||||
|
Res: syscall.RLIMIT_DATA,
|
||||||
|
Rlim: getRlimit(uint64(r.Data)<<10, uint64(r.Data)<<10),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.FileSize > 0 {
|
||||||
|
ret = append(ret, tracee.RLimit{
|
||||||
|
Res: syscall.RLIMIT_FSIZE,
|
||||||
|
Rlim: getRlimit(uint64(r.FileSize)<<10, uint64(r.FileSize)<<10),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.Stack > 0 {
|
||||||
|
ret = append(ret, tracee.RLimit{
|
||||||
|
Res: syscall.RLIMIT_STACK,
|
||||||
|
Rlim: getRlimit(uint64(r.Stack)<<10, uint64(r.Stack)<<10),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.AddressSpace > 0 {
|
||||||
|
ret = append(ret, tracee.RLimit{
|
||||||
|
Res: syscall.RLIMIT_AS,
|
||||||
|
Rlim: getRlimit(uint64(r.AddressSpace)<<10, uint64(r.AddressSpace)<<10),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
63
runprogram/runprogram.go
Normal file
63
runprogram/runprogram.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package runprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/tracer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunProgram defines the spec to run a program safely
|
||||||
|
type RunProgram struct {
|
||||||
|
// argv and env for the child process
|
||||||
|
// work path set by setcwd (current working directory for child)
|
||||||
|
Args []string
|
||||||
|
Env []string
|
||||||
|
WorkDir string
|
||||||
|
|
||||||
|
// file disriptors for new process, from 0 to len - 1
|
||||||
|
Files []uintptr
|
||||||
|
|
||||||
|
// Resource limit set by set rlimit
|
||||||
|
RLimits RLimits
|
||||||
|
|
||||||
|
// Res limit enforced by tracer
|
||||||
|
TraceLimit TraceLimit
|
||||||
|
|
||||||
|
// Allowed / Traced syscall names
|
||||||
|
// Notice: file access syscalls should be traced
|
||||||
|
// If traced syscall is file access, it will checked by file access handler
|
||||||
|
// otherwise it will checked by syscall access handler
|
||||||
|
SyscallAllowed []string
|
||||||
|
SyscallTraced []string
|
||||||
|
|
||||||
|
// Traced syscall handler
|
||||||
|
Handler Handler
|
||||||
|
|
||||||
|
// ShowDetails / Unsafe debug flag
|
||||||
|
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
|
||||||
|
|
||||||
|
// TraceAllow allow the access, trace ban ignores the syscall and set the
|
||||||
|
// return value to BanRet, TraceKill stops the trace action
|
||||||
|
const (
|
||||||
|
TraceAllow = iota + 1
|
||||||
|
TraceBan
|
||||||
|
TraceKill
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler defines the action when a file access encountered
|
||||||
|
type Handler interface {
|
||||||
|
CheckRead(string) TraceAction
|
||||||
|
CheckWrite(string) TraceAction
|
||||||
|
CheckStat(string) TraceAction
|
||||||
|
CheckSyscall(string) TraceAction
|
||||||
|
}
|
||||||
55
runprogram/runprogram_run.go
Normal file
55
runprogram/runprogram_run.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package runprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/secutil"
|
||||||
|
"github.com/criyle/go-judger/tracee"
|
||||||
|
"github.com/criyle/go-judger/tracer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start starts the tracing process
|
||||||
|
func (r *RunProgram) Start() (rt tracer.TraceResult, err error) {
|
||||||
|
// build seccomp filter
|
||||||
|
filter, err := buildFilter(r.ShowDetails, r.SyscallAllowed, r.SyscallTraced)
|
||||||
|
if err != nil {
|
||||||
|
println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer filter.Release()
|
||||||
|
|
||||||
|
bpf, err := secutil.FilterToBPF(filter)
|
||||||
|
if err != nil {
|
||||||
|
println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := &tracee.Runner{
|
||||||
|
Args: r.Args,
|
||||||
|
Env: r.Env,
|
||||||
|
RLimits: r.RLimits.prepareRLimit(),
|
||||||
|
Files: r.Files,
|
||||||
|
WorkDir: r.WorkDir,
|
||||||
|
BPF: bpf,
|
||||||
|
}
|
||||||
|
|
||||||
|
th := &tracerHandler{
|
||||||
|
ShowDetails: r.ShowDetails,
|
||||||
|
Unsafe: r.Unsafe,
|
||||||
|
Handler: r.Handler,
|
||||||
|
}
|
||||||
|
return tracer.Trace(th, ch, tracer.ResLimit(r.TraceLimit))
|
||||||
|
}
|
||||||
|
|
||||||
|
// build filter builds the libseccomp filter according to the allow, trace and show details
|
||||||
|
func buildFilter(showDetails bool, allow, trace []string) (*libseccomp.ScmpFilter, error) {
|
||||||
|
// make filter
|
||||||
|
var defaultAction libseccomp.ScmpAction
|
||||||
|
// if debug, allow all syscalls and output what was blocked
|
||||||
|
if showDetails {
|
||||||
|
defaultAction = libseccomp.ActTrace.SetReturnCode(tracer.MsgDisallow)
|
||||||
|
} else {
|
||||||
|
defaultAction = libseccomp.ActKill
|
||||||
|
}
|
||||||
|
return secutil.BuildFilter(defaultAction, libseccomp.ActTrace.SetReturnCode(tracer.MsgHandle), allow, trace)
|
||||||
|
}
|
||||||
@ -25,7 +25,6 @@ const (
|
|||||||
TraceCodeOLE // 5
|
TraceCodeOLE // 5
|
||||||
TraceCodeBan // 6
|
TraceCodeBan // 6
|
||||||
TraceCodeFatal // 7
|
TraceCodeFatal // 7
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t TraceCode) Error() string {
|
func (t TraceCode) Error() string {
|
||||||
@ -74,4 +73,7 @@ type ResLimit struct {
|
|||||||
type Handler interface {
|
type Handler interface {
|
||||||
Handle(*Context) TraceAction
|
Handle(*Context) TraceAction
|
||||||
GetSyscallName(*Context) (string, error)
|
GetSyscallName(*Context) (string, error)
|
||||||
|
|
||||||
|
Debug(v ...interface{})
|
||||||
|
HandlerDisallow(string) error
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
package tracer
|
package tracer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -16,13 +14,6 @@ const (
|
|||||||
MsgHandle
|
MsgHandle
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// ShowDetails is switch to trun on / off whether to show log message
|
|
||||||
ShowDetails bool
|
|
||||||
// Unsafe determines whether to terminate tracing when bad syscall caught
|
|
||||||
Unsafe bool
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
||||||
@ -41,7 +32,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
|
|
||||||
// Start the runner
|
// Start the runner
|
||||||
pgid, err := runner.Start()
|
pgid, err := runner.Start()
|
||||||
println("tracer started: ", pgid, err)
|
handler.Debug("tracer started: ", pgid, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.TraceStatus = TraceCodeRE
|
result.TraceStatus = TraceCodeRE
|
||||||
return result, err
|
return result, err
|
||||||
@ -61,7 +52,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
err = TraceCodeTLE
|
err = TraceCodeTLE
|
||||||
}
|
}
|
||||||
if err2 := recover(); err2 != nil {
|
if err2 := recover(); err2 != nil {
|
||||||
println(err2)
|
handler.Debug(err2)
|
||||||
err = TraceCodeFatal
|
err = TraceCodeFatal
|
||||||
}
|
}
|
||||||
// kill all tracee upon return
|
// kill all tracee upon return
|
||||||
@ -71,13 +62,19 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
|
|
||||||
// trace unixs
|
// trace unixs
|
||||||
for {
|
for {
|
||||||
// Wait for all child
|
var pid int
|
||||||
pid, err := unix.Wait4(-pgid, &wstatus, unix.WALL, &rusage)
|
if execved {
|
||||||
|
// Wait for all child in the process group
|
||||||
|
pid, err = unix.Wait4(-pgid, &wstatus, unix.WALL, &rusage)
|
||||||
|
} else {
|
||||||
|
// Ensure the process have called setpgid
|
||||||
|
pid, err = unix.Wait4(pgid, &wstatus, unix.WALL, &rusage)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("wait4 failed: ", err)
|
handler.Debug("wait4 failed: ", err)
|
||||||
return result, TraceCodeFatal
|
return result, TraceCodeFatal
|
||||||
}
|
}
|
||||||
println("------ ", pid, " ------")
|
handler.Debug("------ ", pid, " ------")
|
||||||
|
|
||||||
// update resource usage and check against limits
|
// update resource usage and check against limits
|
||||||
userTime := uint(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms
|
userTime := uint(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms
|
||||||
@ -104,7 +101,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
switch {
|
switch {
|
||||||
case wstatus.Exited():
|
case wstatus.Exited():
|
||||||
delete(traced, pid)
|
delete(traced, pid)
|
||||||
println("process exited: ", pid, wstatus.ExitStatus())
|
handler.Debug("process exited: ", pid, wstatus.ExitStatus())
|
||||||
if execved {
|
if execved {
|
||||||
result.ExitCode = wstatus.ExitStatus()
|
result.ExitCode = wstatus.ExitStatus()
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -114,7 +111,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
|
|
||||||
case wstatus.Signaled():
|
case wstatus.Signaled():
|
||||||
sig := wstatus.Signal()
|
sig := wstatus.Signal()
|
||||||
println("ptrace signaled: ", sig)
|
handler.Debug("ptrace signaled: ", sig)
|
||||||
if pid == pgid {
|
if pid == pgid {
|
||||||
switch sig {
|
switch sig {
|
||||||
case unix.SIGXCPU:
|
case unix.SIGXCPU:
|
||||||
@ -134,7 +131,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
case wstatus.Stopped():
|
case wstatus.Stopped():
|
||||||
// Set option if the process is newly forked
|
// Set option if the process is newly forked
|
||||||
if !traced[pid] {
|
if !traced[pid] {
|
||||||
println("set ptrace option")
|
handler.Debug("set ptrace option")
|
||||||
traced[pid] = true
|
traced[pid] = true
|
||||||
// Ptrace set option valid if the tracee is stopped
|
// Ptrace set option valid if the tracee is stopped
|
||||||
err = setPtraceOption(pid)
|
err = setPtraceOption(pid)
|
||||||
@ -156,31 +153,31 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println("ptrace seccomp before execve (should be the execve syscall)")
|
handler.Debug("ptrace seccomp before execve (should be the execve syscall)")
|
||||||
}
|
}
|
||||||
|
|
||||||
case unix.PTRACE_EVENT_CLONE:
|
case unix.PTRACE_EVENT_CLONE:
|
||||||
println("ptrace stop clone")
|
handler.Debug("ptrace stop clone")
|
||||||
case unix.PTRACE_EVENT_VFORK:
|
case unix.PTRACE_EVENT_VFORK:
|
||||||
println("ptrace stop vfork")
|
handler.Debug("ptrace stop vfork")
|
||||||
case unix.PTRACE_EVENT_FORK:
|
case unix.PTRACE_EVENT_FORK:
|
||||||
println("ptrace stop fork")
|
handler.Debug("ptrace stop fork")
|
||||||
case unix.PTRACE_EVENT_EXEC:
|
case unix.PTRACE_EVENT_EXEC:
|
||||||
// forked tracee have successfully called execve
|
// forked tracee have successfully called execve
|
||||||
execved = true
|
execved = true
|
||||||
println("ptrace stop exec")
|
handler.Debug("ptrace stop exec")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
println("ptrace unexpected trap cause: ", trapCause)
|
handler.Debug("ptrace unexpected trap cause: ", trapCause)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Likely encountered SIGSEGV (segment violation)
|
// Likely encountered SIGSEGV (segment violation)
|
||||||
if stopSig != unix.SIGSTOP {
|
if stopSig != unix.SIGSTOP {
|
||||||
println("ptrace unexpected stop signal: ", stopSig)
|
handler.Debug("ptrace unexpected stop signal: ", stopSig)
|
||||||
result.TraceStatus = TraceCodeRE
|
result.TraceStatus = TraceCodeRE
|
||||||
return result, TraceCodeRE
|
return result, TraceCodeRE
|
||||||
}
|
}
|
||||||
println("ptrace stopped")
|
handler.Debug("ptrace stopped")
|
||||||
}
|
}
|
||||||
unix.PtraceCont(pid, 0)
|
unix.PtraceCont(pid, 0)
|
||||||
}
|
}
|
||||||
@ -189,26 +186,22 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
|
|||||||
|
|
||||||
// handleTrap handles the seccomp trap including the custom handle
|
// handleTrap handles the seccomp trap including the custom handle
|
||||||
func handleTrap(handler Handler, pid int) error {
|
func handleTrap(handler Handler, pid int) error {
|
||||||
println("seccomp traced")
|
handler.Debug("seccomp traced")
|
||||||
msg, err := unix.PtraceGetEventMsg(pid)
|
msg, err := unix.PtraceGetEventMsg(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err)
|
handler.Debug(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch int16(msg) {
|
switch int16(msg) {
|
||||||
case MsgDisallow:
|
case MsgDisallow:
|
||||||
ctx, err := getTrapContext(pid)
|
ctx, err := getTrapContext(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err)
|
handler.Debug(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if ShowDetails {
|
syscallName, err := handler.GetSyscallName(ctx)
|
||||||
syscallName, err := handler.GetSyscallName(ctx)
|
handler.Debug("disallowed syscall: ", ctx.SyscallNo(), syscallName, err)
|
||||||
println("disallowed syscall: ", ctx.SyscallNo(), syscallName, err)
|
return handler.HandlerDisallow(syscallName)
|
||||||
}
|
|
||||||
if !Unsafe {
|
|
||||||
return TraceCodeBan
|
|
||||||
}
|
|
||||||
|
|
||||||
case MsgHandle:
|
case MsgHandle:
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
@ -230,7 +223,7 @@ func handleTrap(handler Handler, pid int) error {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// undefined seccomp message, possible set up filter wrong
|
// undefined seccomp message, possible set up filter wrong
|
||||||
println("unknown seccomp trap message: ", msg)
|
handler.Debug("unknown seccomp trap message: ", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -242,13 +235,6 @@ func setPtraceOption(pid int) error {
|
|||||||
unix.PTRACE_O_TRACEFORK|unix.PTRACE_O_TRACECLONE|unix.PTRACE_O_TRACEEXEC|unix.PTRACE_O_TRACEVFORK)
|
unix.PTRACE_O_TRACEFORK|unix.PTRACE_O_TRACECLONE|unix.PTRACE_O_TRACEEXEC|unix.PTRACE_O_TRACEVFORK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// println only print when debug flag is on
|
|
||||||
func println(v ...interface{}) {
|
|
||||||
if ShowDetails {
|
|
||||||
fmt.Fprintln(os.Stderr, v...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// kill all tracee according to pids
|
// kill all tracee according to pids
|
||||||
func killAll(pgid int) {
|
func killAll(pgid int) {
|
||||||
unix.Kill(-pgid, unix.SIGKILL)
|
unix.Kill(-pgid, unix.SIGKILL)
|
||||||
@ -259,10 +245,8 @@ func collectZombie(pgid int) {
|
|||||||
// collect zombies
|
// collect zombies
|
||||||
for {
|
for {
|
||||||
var wstatus unix.WaitStatus
|
var wstatus unix.WaitStatus
|
||||||
if p, err := unix.Wait4(-pgid, &wstatus, unix.WALL|unix.WNOWAIT, nil); err != nil {
|
if _, err := unix.Wait4(-pgid, &wstatus, unix.WALL|unix.WNOWAIT, nil); err != nil {
|
||||||
break
|
break
|
||||||
} else {
|
|
||||||
println("collect: ", p)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user