fix race problem of setpgid and refactor some package

This commit is contained in:
criyle 2019-05-18 16:18:14 -07:00
parent 7cfcc5c391
commit a122ceed5a
18 changed files with 474 additions and 325 deletions

61
cmd/run_program/handle.go Normal file
View 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
}

View File

@ -4,11 +4,9 @@ import (
"flag"
"fmt"
"os"
"runtime"
secutil "github.com/criyle/go-judger/secutil"
tracee "github.com/criyle/go-judger/tracee"
tracer "github.com/criyle/go-judger/tracer"
"github.com/criyle/go-judger/runprogram"
"github.com/criyle/go-judger/tracer"
)
// 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
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
if err != nil {
@ -100,20 +80,28 @@ func main() {
}
}
// Rlimit
rlimit := prepareRLimit(timeLimit, realTimeLimit, outputLimit<<20, stackLimit<<20)
// get tracee
ch := &tracee.Runner{
runner := &runprogram.RunProgram{
Args: args,
Env: []string{"PATH=/"},
RLimits: rlimit,
Files: fds,
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
if result == "stdout" {
@ -129,17 +117,8 @@ func main() {
defer f.Close()
}
tracer.ShowDetails = showDetails
tracer.Unsafe = unsafe
limits := tracer.ResLimit{
TimeLimit: timeLimit * 1e3,
RealTimeLimit: realTimeLimit * 1e3,
MemoryLimit: memoryLimit << 10,
}
// Run tracer
rt, err := tracer.Trace(h, ch, limits)
rt, err := runner.Start()
println("used process_vm_readv: ", tracer.UseVMReadv)
if err != nil {

View 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()
}
}
}

View File

@ -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()
}

View File

@ -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
View 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
View 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
View 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
}

View 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)
}

View File

@ -25,7 +25,6 @@ const (
TraceCodeOLE // 5
TraceCodeBan // 6
TraceCodeFatal // 7
)
func (t TraceCode) Error() string {
@ -74,4 +73,7 @@ type ResLimit struct {
type Handler interface {
Handle(*Context) TraceAction
GetSyscallName(*Context) (string, error)
Debug(v ...interface{})
HandlerDisallow(string) error
}

View File

@ -1,8 +1,6 @@
package tracer
import (
"fmt"
"os"
"runtime"
"time"
@ -16,13 +14,6 @@ const (
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
// this function should called only once and in the same thread that
// exec tracee
@ -41,7 +32,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
// Start the runner
pgid, err := runner.Start()
println("tracer started: ", pgid, err)
handler.Debug("tracer started: ", pgid, err)
if err != nil {
result.TraceStatus = TraceCodeRE
return result, err
@ -61,7 +52,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
err = TraceCodeTLE
}
if err2 := recover(); err2 != nil {
println(err2)
handler.Debug(err2)
err = TraceCodeFatal
}
// kill all tracee upon return
@ -71,13 +62,19 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
// trace unixs
for {
// Wait for all child
pid, err := unix.Wait4(-pgid, &wstatus, unix.WALL, &rusage)
var pid int
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 {
println("wait4 failed: ", err)
handler.Debug("wait4 failed: ", err)
return result, TraceCodeFatal
}
println("------ ", pid, " ------")
handler.Debug("------ ", pid, " ------")
// update resource usage and check against limits
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 {
case wstatus.Exited():
delete(traced, pid)
println("process exited: ", pid, wstatus.ExitStatus())
handler.Debug("process exited: ", pid, wstatus.ExitStatus())
if execved {
result.ExitCode = wstatus.ExitStatus()
return result, nil
@ -114,7 +111,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
case wstatus.Signaled():
sig := wstatus.Signal()
println("ptrace signaled: ", sig)
handler.Debug("ptrace signaled: ", sig)
if pid == pgid {
switch sig {
case unix.SIGXCPU:
@ -134,7 +131,7 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
case wstatus.Stopped():
// Set option if the process is newly forked
if !traced[pid] {
println("set ptrace option")
handler.Debug("set ptrace option")
traced[pid] = true
// Ptrace set option valid if the tracee is stopped
err = setPtraceOption(pid)
@ -156,31 +153,31 @@ func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult,
return result, err
}
} 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:
println("ptrace stop clone")
handler.Debug("ptrace stop clone")
case unix.PTRACE_EVENT_VFORK:
println("ptrace stop vfork")
handler.Debug("ptrace stop vfork")
case unix.PTRACE_EVENT_FORK:
println("ptrace stop fork")
handler.Debug("ptrace stop fork")
case unix.PTRACE_EVENT_EXEC:
// forked tracee have successfully called execve
execved = true
println("ptrace stop exec")
handler.Debug("ptrace stop exec")
default:
println("ptrace unexpected trap cause: ", trapCause)
handler.Debug("ptrace unexpected trap cause: ", trapCause)
}
} else {
// Likely encountered SIGSEGV (segment violation)
if stopSig != unix.SIGSTOP {
println("ptrace unexpected stop signal: ", stopSig)
handler.Debug("ptrace unexpected stop signal: ", stopSig)
result.TraceStatus = TraceCodeRE
return result, TraceCodeRE
}
println("ptrace stopped")
handler.Debug("ptrace stopped")
}
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
func handleTrap(handler Handler, pid int) error {
println("seccomp traced")
handler.Debug("seccomp traced")
msg, err := unix.PtraceGetEventMsg(pid)
if err != nil {
println(err)
handler.Debug(err)
return err
}
switch int16(msg) {
case MsgDisallow:
ctx, err := getTrapContext(pid)
if err != nil {
println(err)
handler.Debug(err)
return err
}
if ShowDetails {
syscallName, err := handler.GetSyscallName(ctx)
println("disallowed syscall: ", ctx.SyscallNo(), syscallName, err)
}
if !Unsafe {
return TraceCodeBan
}
syscallName, err := handler.GetSyscallName(ctx)
handler.Debug("disallowed syscall: ", ctx.SyscallNo(), syscallName, err)
return handler.HandlerDisallow(syscallName)
case MsgHandle:
if handler != nil {
@ -230,7 +223,7 @@ func handleTrap(handler Handler, pid int) error {
default:
// undefined seccomp message, possible set up filter wrong
println("unknown seccomp trap message: ", msg)
handler.Debug("unknown seccomp trap message: ", msg)
}
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)
}
// 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
func killAll(pgid int) {
unix.Kill(-pgid, unix.SIGKILL)
@ -259,10 +245,8 @@ func collectZombie(pgid int) {
// collect zombies
for {
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
} else {
println("collect: ", p)
}
}
}