add debug information

This commit is contained in:
criyle 2019-03-31 20:17:56 -04:00
parent 976f33ead5
commit 05d7ca1edc
4 changed files with 112 additions and 60 deletions

View File

@ -33,6 +33,7 @@ func main() {
t := tracer.NewTracer() t := tracer.NewTracer()
t.Args = os.Args[1:] t.Args = os.Args[1:]
t.TraceHandle = handle t.TraceHandle = handle
//t.Debug = true
rt, err := t.StartTrace() rt, err := t.StartTrace()
log.Println(rt, err) log.Println(rt, err)
} }

View File

@ -56,6 +56,7 @@ const (
TraceCodeMLE TraceCodeMLE
TraceCodeOLE TraceCodeOLE
TraceCodeBan TraceCodeBan
TraceCodeRE
TraceCodeFatal TraceCodeFatal
) )
@ -69,6 +70,8 @@ func (t TraceCode) Error() string {
return "output limit exceeded" return "output limit exceeded"
case TraceCodeBan: case TraceCodeBan:
return "syscall banned" return "syscall banned"
case TraceCodeRE:
return "runtime error"
case TraceCodeFatal: case TraceCodeFatal:
return "handle failed" return "handle failed"
default: default:

View File

@ -2,6 +2,11 @@ package tracer
import libseccomp "github.com/seccomp/libseccomp-golang" import libseccomp "github.com/seccomp/libseccomp-golang"
const (
msgDisallow int16 = iota + 1
msgHandle
)
func addFilterAction(filter *libseccomp.ScmpFilter, name string, action libseccomp.ScmpAction) error { func addFilterAction(filter *libseccomp.ScmpFilter, name string, action libseccomp.ScmpAction) error {
syscallID, err := libseccomp.GetSyscallFromName(name) syscallID, err := libseccomp.GetSyscallFromName(name)
if err != nil { if err != nil {
@ -16,10 +21,10 @@ func addFilterAction(filter *libseccomp.ScmpFilter, name string, action libsecco
func (r *Tracer) buildFilter() (*libseccomp.ScmpFilter, error) { func (r *Tracer) buildFilter() (*libseccomp.ScmpFilter, error) {
// make filter // make filter
//filter, err := libseccomp.NewFilter(libseccomp.ActErrno.SetReturnCode(int16(syscall.EPERM)))
var defaultAction libseccomp.ScmpAction var defaultAction libseccomp.ScmpAction
// if debug, allow all syscalls and output what was blocked
if r.Debug { if r.Debug {
defaultAction = libseccomp.ActTrace.SetReturnCode(100) defaultAction = libseccomp.ActTrace.SetReturnCode(msgDisallow)
} else { } else {
defaultAction = libseccomp.ActKill defaultAction = libseccomp.ActKill
} }
@ -30,15 +35,13 @@ func (r *Tracer) buildFilter() (*libseccomp.ScmpFilter, error) {
for _, s := range r.Allow { for _, s := range r.Allow {
err := addFilterAction(filter, s, libseccomp.ActAllow) err := addFilterAction(filter, s, libseccomp.ActAllow)
//log.Println("[+] allow syscall: ", s)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
for _, s := range r.Trace { for _, s := range r.Trace {
err := addFilterAction(filter, s, libseccomp.ActTrace.SetReturnCode(10)) err := addFilterAction(filter, s, libseccomp.ActTrace.SetReturnCode(msgHandle))
//log.Println("[+] trace syscall: ", s)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -41,11 +41,11 @@ func (r *Tracer) StartTrace() (result *TraceResult, err error) {
return return
} }
log.Println("Strart trace pid: ", pid) r.println("Strart trace pid: ", pid)
// store all tracee process pid existence // store all tracee process pid existence
pids := make(map[int]bool) pids := make(map[int]bool)
//rootPid := pid rootPid := pid
// kill all tracee according to pids // kill all tracee according to pids
killAll := func() { killAll := func() {
@ -57,6 +57,13 @@ func (r *Tracer) StartTrace() (result *TraceResult, err error) {
// kill all tracee upon return // kill all tracee upon return
defer func() { defer func() {
killAll() killAll()
// collect zombies
for {
var wstatus unix.WaitStatus
if _, err := unix.Wait4(-1, &wstatus, unix.WALL|unix.WNOWAIT, nil); err != nil {
break
}
}
}() }()
// Set real time limit, kill process after it // Set real time limit, kill process after it
@ -70,15 +77,19 @@ func (r *Tracer) StartTrace() (result *TraceResult, err error) {
// Trace result // Trace result
var rt TraceResult var rt TraceResult
// If forked tracee execved, this syscall should not be handled
execved := false
// trace unixs // trace unixs
for { for {
// Wait4 return values
var wstatus unix.WaitStatus var wstatus unix.WaitStatus
var rusage unix.Rusage var rusage unix.Rusage
// Wait for all child // Wait for all child
pid, err := unix.Wait4(-1, &wstatus, unix.WALL, &rusage) pid, err := unix.Wait4(-1, &wstatus, unix.WALL, &rusage)
if err != nil { if err != nil {
log.Fatalln("Wait4 fatal: ", err) r.println("Wait4 failed: ", err)
break break
} }
// Set option if the process is newly forked // Set option if the process is newly forked
@ -90,6 +101,7 @@ func (r *Tracer) StartTrace() (result *TraceResult, err error) {
return nil, err return nil, err
} }
} }
// Update resource usage // Update resource usage
rt.UserTime = uint64(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms rt.UserTime = uint64(rusage.Utime.Sec*1e3 + rusage.Utime.Usec/1e3) // ms
rt.UserMem = uint64(rusage.Maxrss) rt.UserMem = uint64(rusage.Maxrss)
@ -102,37 +114,43 @@ func (r *Tracer) StartTrace() (result *TraceResult, err error) {
return nil, TraceCodeMLE return nil, TraceCodeMLE
} }
if wstatus.Exited() { // check process status
switch {
case wstatus.Exited():
delete(pids, pid) delete(pids, pid)
log.Println("Exited", pid, wstatus.ExitStatus()) r.println("Exited", pid, wstatus.ExitStatus())
if pid == rootPid {
if execved {
break break
} }
if wstatus.Signaled() { return nil, TraceCodeFatal
}
continue
case wstatus.Signaled():
sig := wstatus.Signal() sig := wstatus.Signal()
log.Println("Signal", sig) r.println("Signal", sig)
if pid == rootPid {
switch sig { switch sig {
case unix.SIGKILL: case unix.SIGXCPU:
delete(pids, pid) return nil, TraceCodeTLE
log.Println("Killed") case unix.SIGXFSZ:
continue return nil, TraceCodeOLE
case unix.SIGSYS: case unix.SIGSYS:
delete(pids, pid) return nil, TraceCodeBan
log.Println("Blocked syscall")
default: default:
return nil, TraceCodeRE
}
} else {
delete(pids, pid) delete(pids, pid)
continue continue
} }
}
if wstatus.Stopped() {
if wstatus.StopSignal() == unix.SIGTRAP {
switch cause := wstatus.TrapCause(); cause {
case unix.PTRACE_EVENT_SECCOMP:
log.Println("Seccomp Traced")
_, err := unix.PtraceGetEventMsg(pid)
if err != nil {
log.Fatalln(err)
}
case wstatus.Stopped():
if stopSig := wstatus.StopSignal(); stopSig == unix.SIGTRAP {
switch cause := wstatus.TrapCause(); cause {
case unix.PTRACE_EVENT_SECCOMP:
if execved {
// give the customized handle for syscall // give the customized handle for syscall
act, err := r.handleTrap(pid) act, err := r.handleTrap(pid)
if err != nil { if err != nil {
@ -140,41 +158,45 @@ func (r *Tracer) StartTrace() (result *TraceResult, err error) {
} }
switch act { switch act {
case TraceAllow:
// do nothing
case TraceBan: case TraceBan:
// TODO: action according to handler soft ban // TODO: action according to handler soft ban
case TraceKill: case TraceKill:
killAll() return nil, TraceCodeBan
}
} }
case unix.PTRACE_EVENT_CLONE: case unix.PTRACE_EVENT_CLONE:
log.Println("Ptrace stop clone") r.println("Ptrace stop clone")
case unix.PTRACE_EVENT_VFORK: case unix.PTRACE_EVENT_VFORK:
log.Println("Ptrace stop vfork") r.println("Ptrace stop vfork")
case unix.PTRACE_EVENT_FORK: case unix.PTRACE_EVENT_FORK:
log.Println("Ptrace stop fork") r.println("Ptrace stop fork")
case unix.PTRACE_EVENT_EXEC: case unix.PTRACE_EVENT_EXEC:
log.Println("Ptrace stop exec") // forked tracee have successfully called execve
execved = true
r.println("Ptrace stop exec")
default: default:
log.Println("Ptrace trap cause: ", cause, wstatus) r.println("Unexpected ptrace trap cause: ", cause)
} }
} else { } else {
log.Println("Ptrace stop signal: ", wstatus.StopSignal()) r.println("Unexpected ptrace stop signal: ", stopSig)
} }
} }
// continue execution // continue execution
unix.PtraceCont(pid, 0) unix.PtraceCont(pid, 0)
} }
// TODO: finish resource consumption result
return &rt, nil return &rt, nil
} }
// println only print when debug flag is on
func (r *Tracer) println(v ...interface{}) {
if r.Debug {
log.Println(v...)
}
}
func (r *Tracer) verify() { func (r *Tracer) verify() {
if r.RealTimeLimit < r.TimeLimit { if r.RealTimeLimit < r.TimeLimit {
r.RealTimeLimit = r.TimeLimit + 2 r.RealTimeLimit = r.TimeLimit + 2
@ -196,14 +218,37 @@ func (r *Tracer) verify() {
r.Allow = allow r.Allow = allow
} }
// handleTrap handles the seccomp trap including the custom handle
func (r *Tracer) handleTrap(pid int) (TraceAction, error) { func (r *Tracer) handleTrap(pid int) (TraceAction, error) {
r.println("Seccomp Traced")
msg, err := unix.PtraceGetEventMsg(pid)
if err != nil {
r.println(err)
}
switch int16(msg) {
case msgDisallow:
ctx, err := getTrapContext(pid)
if err != nil {
r.println(err)
} else {
syscallNo := ctx.SyscallNo()
syscallName, err := libseccomp.ScmpSyscall(syscallNo).GetName()
r.println("disallowed syscall: ", syscallNo, syscallName, err)
}
case msgHandle:
if r.TraceHandle != nil { if r.TraceHandle != nil {
context, err := getTrapContext(pid) ctx, err := getTrapContext(pid)
if err != nil { if err != nil {
return TraceKill, err return TraceKill, err
} }
return r.TraceHandle(context), nil return r.TraceHandle(ctx), nil
} }
default:
r.println("unknown message: ", msg)
}
return TraceAllow, nil return TraceAllow, nil
} }