mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
make tracee into individual process group
This commit is contained in:
parent
4828363e1d
commit
7cfcc5c391
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,5 +2,5 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Test Env
|
# Test Env
|
||||||
test1/
|
test*/
|
||||||
env.sh
|
env.sh
|
||||||
|
|||||||
@ -132,14 +132,13 @@ func main() {
|
|||||||
tracer.ShowDetails = showDetails
|
tracer.ShowDetails = showDetails
|
||||||
tracer.Unsafe = unsafe
|
tracer.Unsafe = unsafe
|
||||||
limits := tracer.ResLimit{
|
limits := tracer.ResLimit{
|
||||||
TimeLimit: timeLimit * 1e3,
|
TimeLimit: timeLimit * 1e3,
|
||||||
MemoryLimit: memoryLimit << 10,
|
RealTimeLimit: realTimeLimit * 1e3,
|
||||||
|
MemoryLimit: memoryLimit << 10,
|
||||||
}
|
}
|
||||||
runners := []tracer.Runner{ch}
|
|
||||||
|
|
||||||
// Run tracer
|
// Run tracer
|
||||||
results, err := tracer.Trace(h, runners, limits, int64(realTimeLimit)*1e9)
|
rt, err := tracer.Trace(h, ch, limits)
|
||||||
rt := results[0]
|
|
||||||
|
|
||||||
println("used process_vm_readv: ", tracer.UseVMReadv)
|
println("used process_vm_readv: ", tracer.UseVMReadv)
|
||||||
|
|
||||||
|
|||||||
@ -89,6 +89,13 @@ func (r *Runner) Start() (int, error) {
|
|||||||
afterForkInChild()
|
afterForkInChild()
|
||||||
// Notice: cannot call any GO functions beyond this point
|
// Notice: cannot call any GO functions beyond this point
|
||||||
|
|
||||||
|
// Set the pgid, so that the wait operation can apply to only certain
|
||||||
|
// subgroup of processes
|
||||||
|
_, _, err1 = syscall.RawSyscall(syscall.SYS_SETPGID, 0, 0, 0)
|
||||||
|
if err1 != 0 {
|
||||||
|
goto childerror
|
||||||
|
}
|
||||||
|
|
||||||
// Set limit
|
// Set limit
|
||||||
for _, rlim := range r.RLimits {
|
for _, rlim := range r.RLimits {
|
||||||
// Prlimit instead of setrlimit to avoid 32-bit limitation (linux > 3.2)
|
// Prlimit instead of setrlimit to avoid 32-bit limitation (linux > 3.2)
|
||||||
|
|||||||
@ -65,8 +65,9 @@ type Runner interface {
|
|||||||
|
|
||||||
// ResLimit represents the resource limit for traced process
|
// ResLimit represents the resource limit for traced process
|
||||||
type ResLimit struct {
|
type ResLimit struct {
|
||||||
TimeLimit uint // user CPU time limit (in ms)
|
TimeLimit uint // user CPU time limit (in ms)
|
||||||
MemoryLimit uint // user memory limit (in kB)
|
RealTimeLimit uint // sig_kill will force the process to exit after this limit (in ms)
|
||||||
|
MemoryLimit uint // user memory limit (in kB)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler defines customized handler for traced syscall
|
// Handler defines customized handler for traced syscall
|
||||||
|
|||||||
@ -26,36 +26,31 @@ var (
|
|||||||
// 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
|
||||||
func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (results []TraceResult, err error) {
|
func Trace(handler Handler, runner Runner, limits ResLimit) (result TraceResult, err error) {
|
||||||
var (
|
var (
|
||||||
wstatus unix.WaitStatus // wait4 wait status
|
wstatus unix.WaitStatus // wait4 wait status
|
||||||
rusage unix.Rusage // wait4 rusage
|
rusage unix.Rusage // wait4 rusage
|
||||||
tle bool // whether the timmer triggered due to timeout
|
tle bool // whether the timmer triggered due to timeout
|
||||||
traced = make(map[int]bool) // store all process that have set ptrace options
|
traced = make(map[int]bool) // store all process that have set ptrace options
|
||||||
execved = make(map[int]bool) // store whether a runner process have successfully execvd
|
execved = false // store whether the runner process have successfully execvd
|
||||||
pidmap = make(map[int]int) // pid -> index map
|
|
||||||
running = len(runners) // total number of remained runner process
|
|
||||||
)
|
)
|
||||||
results = make([]TraceResult, len(runners))
|
|
||||||
|
|
||||||
// make this thread exit after trace, ensure no process escaped
|
// ptrace is thread based (kernel proc)
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
// Starts all runners
|
// Start the runner
|
||||||
for i, r := range runners {
|
pgid, err := runner.Start()
|
||||||
pid, err := r.Start()
|
println("tracer started: ", pgid, err)
|
||||||
println("tracer started: ", pid, err)
|
if err != nil {
|
||||||
if err != nil {
|
result.TraceStatus = TraceCodeRE
|
||||||
results[i].TraceStatus = TraceCodeRE
|
return result, err
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
pidmap[pid] = i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set real time limit, kill process after it
|
// Set real time limit, kill process after it
|
||||||
timer := time.AfterFunc(time.Duration(timeout), func() {
|
timer := time.AfterFunc(time.Duration(limits.RealTimeLimit*1e6), func() {
|
||||||
tle = true
|
tle = true
|
||||||
killAll(traced)
|
killAll(pgid)
|
||||||
})
|
})
|
||||||
|
|
||||||
// handler potential panic and tle
|
// handler potential panic and tle
|
||||||
@ -70,24 +65,23 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
err = TraceCodeFatal
|
err = TraceCodeFatal
|
||||||
}
|
}
|
||||||
// kill all tracee upon return
|
// kill all tracee upon return
|
||||||
killAll(traced)
|
killAll(pgid)
|
||||||
collectZombie()
|
collectZombie(pgid)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// trace unixs
|
// trace unixs
|
||||||
for {
|
for {
|
||||||
// Wait for all child
|
// Wait for all child
|
||||||
pid, err := unix.Wait4(-1, &wstatus, unix.WALL, &rusage)
|
pid, err := unix.Wait4(-pgid, &wstatus, unix.WALL, &rusage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("wait4 failed: ", err)
|
println("wait4 failed: ", err)
|
||||||
return results, TraceCodeFatal
|
return result, TraceCodeFatal
|
||||||
}
|
}
|
||||||
println("------ ", pid, " ------")
|
println("------ ", 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
|
||||||
userMem := uint(rusage.Maxrss) // kb
|
userMem := uint(rusage.Maxrss) // kb
|
||||||
idx, inside := pidmap[pid] // index
|
|
||||||
status := TraceCodeNormal // check limit
|
status := TraceCodeNormal // check limit
|
||||||
|
|
||||||
// check tle / mle
|
// check tle / mle
|
||||||
@ -97,15 +91,13 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
if userMem > limits.MemoryLimit {
|
if userMem > limits.MemoryLimit {
|
||||||
status = TraceCodeMLE
|
status = TraceCodeMLE
|
||||||
}
|
}
|
||||||
if inside {
|
result = TraceResult{
|
||||||
results[idx] = TraceResult{
|
UserTime: userTime,
|
||||||
UserTime: userTime,
|
UserMem: userMem,
|
||||||
UserMem: userMem,
|
TraceStatus: status,
|
||||||
TraceStatus: status,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if status != TraceCodeNormal {
|
if status != TraceCodeNormal {
|
||||||
return results, status
|
return result, status
|
||||||
}
|
}
|
||||||
|
|
||||||
// check process status
|
// check process status
|
||||||
@ -113,22 +105,17 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
case wstatus.Exited():
|
case wstatus.Exited():
|
||||||
delete(traced, pid)
|
delete(traced, pid)
|
||||||
println("process exited: ", pid, wstatus.ExitStatus())
|
println("process exited: ", pid, wstatus.ExitStatus())
|
||||||
if inside {
|
if execved {
|
||||||
if execved[pid] {
|
result.ExitCode = wstatus.ExitStatus()
|
||||||
results[idx].ExitCode = wstatus.ExitStatus()
|
return result, nil
|
||||||
if running--; running == 0 {
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results[idx].TraceStatus = TraceCodeFatal
|
|
||||||
return results, TraceCodeFatal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
result.TraceStatus = TraceCodeFatal
|
||||||
|
return result, TraceCodeFatal
|
||||||
|
|
||||||
case wstatus.Signaled():
|
case wstatus.Signaled():
|
||||||
sig := wstatus.Signal()
|
sig := wstatus.Signal()
|
||||||
println("ptrace signaled: ", sig)
|
println("ptrace signaled: ", sig)
|
||||||
if inside {
|
if pid == pgid {
|
||||||
switch sig {
|
switch sig {
|
||||||
case unix.SIGXCPU:
|
case unix.SIGXCPU:
|
||||||
status = TraceCodeTLE
|
status = TraceCodeTLE
|
||||||
@ -139,8 +126,8 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
default:
|
default:
|
||||||
status = TraceCodeRE
|
status = TraceCodeRE
|
||||||
}
|
}
|
||||||
results[idx].TraceStatus = status
|
result.TraceStatus = status
|
||||||
return results, status
|
return result, status
|
||||||
}
|
}
|
||||||
delete(traced, pid)
|
delete(traced, pid)
|
||||||
|
|
||||||
@ -152,7 +139,8 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
// Ptrace set option valid if the tracee is stopped
|
// Ptrace set option valid if the tracee is stopped
|
||||||
err = setPtraceOption(pid)
|
err = setPtraceOption(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return results, err
|
result.TraceStatus = TraceCodeFatal
|
||||||
|
return result, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,14 +148,12 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
if stopSig := wstatus.StopSignal(); stopSig == unix.SIGTRAP {
|
if stopSig := wstatus.StopSignal(); stopSig == unix.SIGTRAP {
|
||||||
switch trapCause := wstatus.TrapCause(); trapCause {
|
switch trapCause := wstatus.TrapCause(); trapCause {
|
||||||
case unix.PTRACE_EVENT_SECCOMP:
|
case unix.PTRACE_EVENT_SECCOMP:
|
||||||
if !inside || execved[pid] {
|
if execved {
|
||||||
// give the customized handle for syscall
|
// give the customized handle for syscall
|
||||||
err := handleTrap(handler, pid)
|
err := handleTrap(handler, pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if inside {
|
result.TraceStatus = TraceCodeBan
|
||||||
results[idx].TraceStatus = TraceCodeBan
|
return result, err
|
||||||
}
|
|
||||||
return results, err
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println("ptrace seccomp before execve (should be the execve syscall)")
|
println("ptrace seccomp before execve (should be the execve syscall)")
|
||||||
@ -181,7 +167,7 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
println("ptrace stop fork")
|
println("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[pid] = true
|
execved = true
|
||||||
println("ptrace stop exec")
|
println("ptrace stop exec")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -191,19 +177,12 @@ func Trace(handler Handler, runners []Runner, limits ResLimit, timeout int64) (r
|
|||||||
// Likely encountered SIGSEGV (segment violation)
|
// Likely encountered SIGSEGV (segment violation)
|
||||||
if stopSig != unix.SIGSTOP {
|
if stopSig != unix.SIGSTOP {
|
||||||
println("ptrace unexpected stop signal: ", stopSig)
|
println("ptrace unexpected stop signal: ", stopSig)
|
||||||
if inside {
|
result.TraceStatus = TraceCodeRE
|
||||||
results[idx].TraceStatus = TraceCodeRE
|
return result, TraceCodeRE
|
||||||
}
|
|
||||||
return results, TraceCodeRE
|
|
||||||
}
|
}
|
||||||
println("ptrace stopped")
|
println("ptrace stopped")
|
||||||
}
|
}
|
||||||
unix.PtraceCont(pid, 0)
|
unix.PtraceCont(pid, 0)
|
||||||
|
|
||||||
default:
|
|
||||||
// should never happen
|
|
||||||
println("unexpected wait status: ", wstatus)
|
|
||||||
unix.PtraceCont(pid, 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,19 +250,16 @@ func println(v ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// kill all tracee according to pids
|
// kill all tracee according to pids
|
||||||
func killAll(pids map[int]bool) {
|
func killAll(pgid int) {
|
||||||
for p := range pids {
|
unix.Kill(-pgid, unix.SIGKILL)
|
||||||
println("kill: ", p)
|
|
||||||
unix.Kill(p, unix.SIGKILL)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect died child processes
|
// collect died child processes
|
||||||
func collectZombie() {
|
func collectZombie(pgid int) {
|
||||||
// collect zombies
|
// collect zombies
|
||||||
for {
|
for {
|
||||||
var wstatus unix.WaitStatus
|
var wstatus unix.WaitStatus
|
||||||
if p, err := unix.Wait4(-1, &wstatus, unix.WALL|unix.WNOWAIT, nil); err != nil {
|
if p, err := unix.Wait4(-pgid, &wstatus, unix.WALL|unix.WNOWAIT, nil); err != nil {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
println("collect: ", p)
|
println("collect: ", p)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user