mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
Refactor seccomp ptrace & improve container
This commit is contained in:
parent
3c77bf81d1
commit
7603109e33
@ -163,7 +163,7 @@ type Environment interface {
|
||||
|
||||
## Packages
|
||||
|
||||
- config: defines arch & language specified trace condition for ptrace runner from UOJ
|
||||
- cmd/runprog/config: defines arch & language specified trace condition for ptrace runner from UOJ
|
||||
- container: creates pre-forked container to run programs inside
|
||||
- runner: interface to run program
|
||||
- ptrace: wrapper to call forkexec and ptracer
|
||||
|
||||
@ -37,6 +37,8 @@ var (
|
||||
"ioctl",
|
||||
"fcntl",
|
||||
"fadvise64",
|
||||
"pread64",
|
||||
"pwrite64",
|
||||
|
||||
// memory action
|
||||
"mmap",
|
||||
@ -13,14 +13,13 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-sandbox/config"
|
||||
"github.com/criyle/go-sandbox/cmd/runprog/config"
|
||||
"github.com/criyle/go-sandbox/container"
|
||||
"github.com/criyle/go-sandbox/pkg/cgroup"
|
||||
"github.com/criyle/go-sandbox/pkg/forkexec"
|
||||
"github.com/criyle/go-sandbox/pkg/memfd"
|
||||
"github.com/criyle/go-sandbox/pkg/mount"
|
||||
"github.com/criyle/go-sandbox/pkg/rlimit"
|
||||
"github.com/criyle/go-sandbox/pkg/seccomp"
|
||||
"github.com/criyle/go-sandbox/pkg/seccomp/libseccomp"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
"github.com/criyle/go-sandbox/runner/ptrace"
|
||||
@ -245,9 +244,9 @@ func start() (*runner.Result, error) {
|
||||
}
|
||||
debug("rlimit: ", rlims)
|
||||
|
||||
actionDefault := seccomp.ActionKill
|
||||
actionDefault := libseccomp.ActionKill
|
||||
if showDetails {
|
||||
actionDefault = seccomp.ActionTrace.WithReturnCode(seccomp.MsgDisallow)
|
||||
actionDefault = libseccomp.ActionTrace
|
||||
}
|
||||
if runt != "ptrace" {
|
||||
allow = append(allow, trace...)
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
package container
|
||||
|
||||
const (
|
||||
cmdPing = "ping"
|
||||
cmdCopyIn = "copyin"
|
||||
cmdOpen = "open"
|
||||
cmdDelete = "delete"
|
||||
cmdReset = "reset"
|
||||
cmdExecve = "execve"
|
||||
cmdOk = "ok"
|
||||
cmdKill = "kill"
|
||||
cmdConf = "conf"
|
||||
cmdPing cmdType = iota + 1
|
||||
cmdCopyIn
|
||||
cmdOpen
|
||||
cmdDelete
|
||||
cmdReset
|
||||
cmdExecve
|
||||
cmdOk
|
||||
cmdKill
|
||||
cmdConf
|
||||
|
||||
initArg = "container_init"
|
||||
|
||||
|
||||
@ -92,7 +92,10 @@ func (c *containerServer) handleExecve(cmd *execCmd, msg *unixsocket.Msg) error
|
||||
// cannot exists now since host assumes the start will alway work
|
||||
err = fmt.Errorf("execve: start: %v", err)
|
||||
}
|
||||
return c.handleExecveStarted(pid, err)
|
||||
}
|
||||
|
||||
func (c *containerServer) handleExecveStarted(pid int, err error) error {
|
||||
// done is to signal kill goroutine exits
|
||||
killDone := make(chan struct{})
|
||||
// waitDone is to signal kill goroutine to collect zombies
|
||||
|
||||
@ -90,7 +90,7 @@ func (c *containerServer) handleCmd(cmd *cmd, msg *unixsocket.Msg) error {
|
||||
case cmdExecve:
|
||||
return c.handleExecve(cmd.ExecCmd, msg)
|
||||
}
|
||||
return fmt.Errorf("unknown command: %s", cmd.Cmd)
|
||||
return fmt.Errorf("unknown command: %v", cmd.Cmd)
|
||||
}
|
||||
|
||||
func initContainer(c containerConfig) error {
|
||||
|
||||
@ -11,9 +11,11 @@ import (
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
)
|
||||
|
||||
type cmdType int
|
||||
|
||||
// cmd is the control message send into container
|
||||
type cmd struct {
|
||||
Cmd string // type of the cmd
|
||||
Cmd cmdType // type of the cmd
|
||||
|
||||
OpenCmd []OpenCmd // open argument
|
||||
DeleteCmd *deleteCmd // delete argument
|
||||
|
||||
7
go.mod
7
go.mod
@ -8,10 +8,9 @@ require (
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/stretchr/testify v1.6.1 // indirect
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
)
|
||||
|
||||
replace github.com/elastic/go-seccomp-bpf => ../go-seccomp-bpf
|
||||
|
||||
13
go.sum
13
go.sum
@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elastic/go-seccomp-bpf v1.1.0 h1:jUzzDc6LyCtdolZdvL/26dad6rZ9vsc7xZ2eadKECAU=
|
||||
github.com/elastic/go-seccomp-bpf v1.1.0/go.mod h1:l+89Vy5BzjVcaX8USZRMOwmwwDScE+vxCFzzvQwN7T8=
|
||||
github.com/elastic/go-ucfg v0.7.0/go.mod h1:iaiY0NBIYeasNgycLyTvhJftQlQEUO2hpF+FX0JKxzo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@ -17,6 +19,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@ -24,16 +27,26 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package seccomp
|
||||
package libseccomp
|
||||
|
||||
// Action is seccomp trap action
|
||||
type Action uint32
|
||||
@ -19,16 +19,6 @@ const (
|
||||
MsgHandle
|
||||
)
|
||||
|
||||
// WithReturnCode set the return code when action is trace or ban
|
||||
func (a Action) WithReturnCode(code int16) Action {
|
||||
return a.Action() | Action(code)<<16
|
||||
}
|
||||
|
||||
// ReturnCode get the return code
|
||||
func (a Action) ReturnCode() int16 {
|
||||
return int16(a >> 16)
|
||||
}
|
||||
|
||||
// Action get the basic action
|
||||
func (a Action) Action() Action {
|
||||
return Action(a & 0xffff)
|
||||
@ -1,25 +1,24 @@
|
||||
package libseccomp
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-sandbox/pkg/seccomp"
|
||||
libseccomp "github.com/elastic/go-seccomp-bpf"
|
||||
)
|
||||
|
||||
// ToSeccompAction convert action to libseccomp compatible action
|
||||
func ToSeccompAction(a seccomp.Action) libseccomp.Action {
|
||||
func ToSeccompAction(a Action) libseccomp.Action {
|
||||
var action libseccomp.Action
|
||||
switch a.Action() {
|
||||
case seccomp.ActionAllow:
|
||||
case ActionAllow:
|
||||
action = libseccomp.ActionAllow
|
||||
case seccomp.ActionErrno:
|
||||
case ActionErrno:
|
||||
action = libseccomp.ActionErrno
|
||||
case seccomp.ActionTrace:
|
||||
case ActionTrace:
|
||||
action = libseccomp.ActionTrace
|
||||
default:
|
||||
action = libseccomp.ActionKillProcess
|
||||
}
|
||||
// the least 16 bit of ret value is SECCOMP_RET_DATA
|
||||
// although it might not officially supported by go-seccomp-bpf
|
||||
action = action.WithReturnData(int(a.ReturnCode()))
|
||||
// action = action.WithReturnData(int(a.ReturnCode()))
|
||||
return action
|
||||
}
|
||||
|
||||
@ -11,10 +11,10 @@ import (
|
||||
// Builder is used to build the filter
|
||||
type Builder struct {
|
||||
Allow, Trace []string
|
||||
Default seccomp.Action
|
||||
Default Action
|
||||
}
|
||||
|
||||
var actTrace = libseccomp.ActionTrace | libseccomp.Action(seccomp.MsgHandle)
|
||||
var actTrace = libseccomp.ActionTrace
|
||||
|
||||
// Build builds the filter
|
||||
func (b *Builder) Build() (seccomp.Filter, error) {
|
||||
|
||||
@ -33,7 +33,7 @@ func BenchmarkBuildDefaultFilter(b *testing.B) {
|
||||
builder := Builder{
|
||||
Allow: defaultSyscallAllows,
|
||||
Trace: defaultSyscallTraces,
|
||||
Default: seccomp.ActionTrace,
|
||||
Default: ActionTrace,
|
||||
}
|
||||
builder.Build()
|
||||
}
|
||||
@ -43,7 +43,7 @@ func buildFilterMock() (seccomp.Filter, error) {
|
||||
b := Builder{
|
||||
Allow: []string{"fork"},
|
||||
Trace: []string{"execve"},
|
||||
Default: seccomp.ActionTrace,
|
||||
Default: ActionTrace,
|
||||
}
|
||||
return b.Build()
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ type TraceAction int
|
||||
const (
|
||||
// TraceAllow does not do anything
|
||||
TraceAllow TraceAction = iota
|
||||
// TraceBan blocked the syscall and set the return code specified by SetReturnCode
|
||||
// TraceBan skippes the syscall and set the return code specified by SetReturnCode
|
||||
TraceBan
|
||||
// TraceKill referred as dangerous action have been detected
|
||||
TraceKill
|
||||
@ -24,14 +24,15 @@ type Tracer struct {
|
||||
// Runner represents the process runner
|
||||
type Runner interface {
|
||||
// Starts starts the child process and return pid and error if failed
|
||||
// the child process should enable ptrace and should stop before ptrace
|
||||
Start() (int, error)
|
||||
}
|
||||
|
||||
// Handler defines customized handler for traced syscall
|
||||
type Handler interface {
|
||||
// Handle returns action take to the traced program
|
||||
Handle(*Context) TraceAction
|
||||
GetSyscallName(*Context) (string, error)
|
||||
|
||||
// Debug prints debug information when in debug mode
|
||||
Debug(v ...interface{})
|
||||
HandlerDisallow(string) error
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
|
||||
unix "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/criyle/go-sandbox/pkg/seccomp"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
)
|
||||
|
||||
@ -24,17 +23,6 @@ func (t *Tracer) Trace(c context.Context) <-chan runner.Result {
|
||||
// TraceRun start and traces all child process by runner in the calling goroutine
|
||||
// parameter done used to cancel work, start is used notify child starts
|
||||
func (t *Tracer) TraceRun(c context.Context) (result runner.Result) {
|
||||
var (
|
||||
status = runner.StatusNormal
|
||||
wstatus unix.WaitStatus // wait4 wait status
|
||||
rusage unix.Rusage // wait4 rusage
|
||||
traced = make(map[int]bool) // store all process that have set ptrace options
|
||||
execved = false // store whether the runner process have successfully execvd
|
||||
pid int // store pid of wait4 result
|
||||
sTime = time.Now() // records start time for trace process
|
||||
fTime time.Time // records finish time for execve
|
||||
)
|
||||
|
||||
// ptrace is thread based (kernel proc)
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
@ -48,7 +36,10 @@ func (t *Tracer) TraceRun(c context.Context) (result runner.Result) {
|
||||
result.Error = err.Error()
|
||||
return
|
||||
}
|
||||
return t.trace(c, pgid)
|
||||
}
|
||||
|
||||
func (t *Tracer) trace(c context.Context, pgid int) (result runner.Result) {
|
||||
cc, cancel := context.WithCancel(c)
|
||||
defer cancel()
|
||||
|
||||
@ -58,6 +49,9 @@ func (t *Tracer) TraceRun(c context.Context) (result runner.Result) {
|
||||
killAll(pgid)
|
||||
}()
|
||||
|
||||
sTime := time.Now()
|
||||
ph := newPtraceHandle(t, pgid)
|
||||
|
||||
// handler potential panic and tle
|
||||
// also ensure processes was well terminated
|
||||
defer func() {
|
||||
@ -69,13 +63,21 @@ func (t *Tracer) TraceRun(c context.Context) (result runner.Result) {
|
||||
// kill all tracee upon return
|
||||
killAll(pgid)
|
||||
collectZombie(pgid)
|
||||
result.SetUpTime = fTime.Sub(sTime)
|
||||
result.RunningTime = time.Since(fTime)
|
||||
if !ph.fTime.IsZero() {
|
||||
result.SetUpTime = ph.fTime.Sub(sTime)
|
||||
result.RunningTime = time.Since(ph.fTime)
|
||||
}
|
||||
}()
|
||||
|
||||
// ptrace pool loop
|
||||
for {
|
||||
if execved {
|
||||
var (
|
||||
wstatus unix.WaitStatus // wait4 wait status
|
||||
rusage unix.Rusage // wait4 rusage
|
||||
pid int // store pid of wait4 result
|
||||
err error
|
||||
)
|
||||
if ph.execved {
|
||||
// Wait for all child in the process group
|
||||
pid, err = unix.Wait4(-pgid, &wstatus, unix.WALL, &rusage)
|
||||
} else {
|
||||
@ -94,184 +96,194 @@ func (t *Tracer) TraceRun(c context.Context) (result runner.Result) {
|
||||
}
|
||||
t.Handler.Debug("------ ", pid, " ------")
|
||||
|
||||
// update rusage
|
||||
if pid == pgid {
|
||||
// update resource usage and check against limits
|
||||
userTime := time.Duration(rusage.Utime.Nano()) // ns
|
||||
userMem := runner.Size(rusage.Maxrss << 10) // bytes
|
||||
|
||||
// check tle / mle
|
||||
if userTime > t.Limit.TimeLimit {
|
||||
status = runner.StatusTimeLimitExceeded
|
||||
}
|
||||
if userMem > t.Limit.MemoryLimit {
|
||||
status = runner.StatusMemoryLimitExceeded
|
||||
}
|
||||
result = runner.Result{
|
||||
Status: status,
|
||||
Time: userTime,
|
||||
Memory: userMem,
|
||||
}
|
||||
if status != runner.StatusNormal {
|
||||
userTime, userMem, curStatus := t.checkUsage(rusage)
|
||||
result.Status = curStatus
|
||||
result.Time = userTime
|
||||
result.Memory = userMem
|
||||
if curStatus != runner.StatusNormal {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check process status
|
||||
switch {
|
||||
case wstatus.Exited():
|
||||
delete(traced, pid)
|
||||
t.Handler.Debug("process exited: ", pid, wstatus.ExitStatus())
|
||||
if pid == pgid {
|
||||
if execved {
|
||||
result.ExitStatus = wstatus.ExitStatus()
|
||||
if result.ExitStatus == 0 {
|
||||
result.Status = runner.StatusNormal
|
||||
} else {
|
||||
result.Status = runner.StatusNonzeroExitStatus
|
||||
}
|
||||
return
|
||||
}
|
||||
result.Status = runner.StatusRunnerError
|
||||
result.Error = "child process exit before execve"
|
||||
return
|
||||
}
|
||||
|
||||
case wstatus.Signaled():
|
||||
sig := wstatus.Signal()
|
||||
t.Handler.Debug("ptrace signaled: ", sig)
|
||||
if pid == pgid {
|
||||
delete(traced, pid)
|
||||
switch sig {
|
||||
case unix.SIGXCPU, unix.SIGKILL:
|
||||
status = runner.StatusTimeLimitExceeded
|
||||
case unix.SIGXFSZ:
|
||||
status = runner.StatusOutputLimitExceeded
|
||||
case unix.SIGSYS:
|
||||
status = runner.StatusDisallowedSyscall
|
||||
default:
|
||||
status = runner.StatusSignalled
|
||||
}
|
||||
result.Status = status
|
||||
result.ExitStatus = int(sig)
|
||||
return
|
||||
}
|
||||
unix.PtraceCont(pid, int(sig))
|
||||
|
||||
case wstatus.Stopped():
|
||||
// Set option if the process is newly forked
|
||||
if !traced[pid] {
|
||||
t.Handler.Debug("set ptrace option for", pid)
|
||||
traced[pid] = true
|
||||
// Ptrace set option valid if the tracee is stopped
|
||||
err = setPtraceOption(pid)
|
||||
if err != nil {
|
||||
result.Status = runner.StatusRunnerError
|
||||
result.Error = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check stop signal, if trap then check seccomp
|
||||
if stopSig := wstatus.StopSignal(); stopSig == unix.SIGTRAP {
|
||||
switch trapCause := wstatus.TrapCause(); trapCause {
|
||||
case unix.PTRACE_EVENT_SECCOMP:
|
||||
if execved {
|
||||
// give the customized handle for syscall
|
||||
err := t.handleTrap(pid)
|
||||
if err != nil {
|
||||
result.Status = runner.StatusDisallowedSyscall
|
||||
result.Error = err.Error()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
t.Handler.Debug("ptrace seccomp before execve (should be the execve syscall)")
|
||||
}
|
||||
|
||||
case unix.PTRACE_EVENT_CLONE:
|
||||
t.Handler.Debug("ptrace stop clone")
|
||||
case unix.PTRACE_EVENT_VFORK:
|
||||
t.Handler.Debug("ptrace stop vfork")
|
||||
case unix.PTRACE_EVENT_FORK:
|
||||
t.Handler.Debug("ptrace stop fork")
|
||||
case unix.PTRACE_EVENT_EXEC:
|
||||
// forked tracee have successfully called execve
|
||||
if !execved {
|
||||
fTime = time.Now()
|
||||
execved = true
|
||||
}
|
||||
t.Handler.Debug("ptrace stop exec")
|
||||
|
||||
default:
|
||||
t.Handler.Debug("ptrace unexpected trap cause: ", trapCause)
|
||||
}
|
||||
unix.PtraceCont(pid, 0)
|
||||
} else {
|
||||
// check if cpu rlimit hit
|
||||
switch stopSig {
|
||||
case unix.SIGXCPU:
|
||||
status = runner.StatusTimeLimitExceeded
|
||||
case unix.SIGXFSZ:
|
||||
status = runner.StatusOutputLimitExceeded
|
||||
}
|
||||
if status != runner.StatusNormal {
|
||||
result.Status = status
|
||||
return
|
||||
}
|
||||
// Likely encountered SIGSEGV (segment violation)
|
||||
// Or compiler child exited
|
||||
if stopSig != unix.SIGSTOP {
|
||||
t.Handler.Debug("ptrace unexpected stop signal: ", stopSig)
|
||||
}
|
||||
t.Handler.Debug("ptrace stopped")
|
||||
unix.PtraceCont(pid, int(stopSig))
|
||||
}
|
||||
status, exitStatus, errStr, finished := ph.handle(pid, wstatus)
|
||||
if finished || status != runner.StatusNormal {
|
||||
result.Status = status
|
||||
result.ExitStatus = exitStatus
|
||||
result.Error = errStr
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleTrap handles the seccomp trap including the custom handle
|
||||
func (t *Tracer) handleTrap(pid int) error {
|
||||
t.Handler.Debug("seccomp traced")
|
||||
msg, err := unix.PtraceGetEventMsg(pid)
|
||||
if err != nil {
|
||||
t.Handler.Debug("PtraceGetEventMsg failed:", err)
|
||||
return err
|
||||
func (t *Tracer) checkUsage(rusage unix.Rusage) (time.Duration, runner.Size, runner.Status) {
|
||||
status := runner.StatusNormal
|
||||
// update resource usage and check against limits
|
||||
userTime := time.Duration(rusage.Utime.Nano()) // ns
|
||||
userMem := runner.Size(rusage.Maxrss << 10) // bytes
|
||||
|
||||
// check tle / mle
|
||||
if userTime > t.Limit.TimeLimit {
|
||||
status = runner.StatusTimeLimitExceeded
|
||||
}
|
||||
switch int16(msg) {
|
||||
case seccomp.MsgDisallow:
|
||||
if userMem > t.Limit.MemoryLimit {
|
||||
status = runner.StatusMemoryLimitExceeded
|
||||
}
|
||||
return userTime, userMem, status
|
||||
}
|
||||
|
||||
type ptraceHandle struct {
|
||||
*Tracer
|
||||
pgid int
|
||||
traced map[int]bool
|
||||
execved bool
|
||||
fTime time.Time
|
||||
}
|
||||
|
||||
func newPtraceHandle(t *Tracer, pgid int) *ptraceHandle {
|
||||
return &ptraceHandle{t, pgid, make(map[int]bool), false, time.Time{}}
|
||||
}
|
||||
|
||||
func (ph *ptraceHandle) handle(pid int, wstatus unix.WaitStatus) (status runner.Status, exitStatus int, errStr string, finished bool) {
|
||||
status = runner.StatusNormal
|
||||
// check process status
|
||||
switch {
|
||||
case wstatus.Exited():
|
||||
delete(ph.traced, pid)
|
||||
ph.Handler.Debug("process exited: ", pid, wstatus.ExitStatus())
|
||||
if pid == ph.pgid {
|
||||
finished = true
|
||||
if ph.execved {
|
||||
exitStatus = wstatus.ExitStatus()
|
||||
if exitStatus == 0 {
|
||||
status = runner.StatusNormal
|
||||
} else {
|
||||
status = runner.StatusNonzeroExitStatus
|
||||
}
|
||||
return
|
||||
}
|
||||
status = runner.StatusRunnerError
|
||||
errStr = "child process exit before execve"
|
||||
return
|
||||
}
|
||||
|
||||
case wstatus.Signaled():
|
||||
sig := wstatus.Signal()
|
||||
ph.Handler.Debug("ptrace signaled: ", sig)
|
||||
if pid == ph.pgid {
|
||||
delete(ph.traced, pid)
|
||||
switch sig {
|
||||
case unix.SIGXCPU, unix.SIGKILL:
|
||||
status = runner.StatusTimeLimitExceeded
|
||||
case unix.SIGXFSZ:
|
||||
status = runner.StatusOutputLimitExceeded
|
||||
case unix.SIGSYS:
|
||||
status = runner.StatusDisallowedSyscall
|
||||
default:
|
||||
status = runner.StatusSignalled
|
||||
}
|
||||
exitStatus = int(sig)
|
||||
return
|
||||
}
|
||||
unix.PtraceCont(pid, int(sig))
|
||||
|
||||
case wstatus.Stopped():
|
||||
// Set option if the process is newly forked
|
||||
if !ph.traced[pid] {
|
||||
ph.Handler.Debug("set ptrace option for", pid)
|
||||
ph.traced[pid] = true
|
||||
// Ptrace set option valid if the tracee is stopped
|
||||
if err := setPtraceOption(pid); err != nil {
|
||||
status = runner.StatusRunnerError
|
||||
errStr = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
stopSig := wstatus.StopSignal()
|
||||
// Check stop signal, if trap then check seccomp
|
||||
switch stopSig {
|
||||
case unix.SIGTRAP:
|
||||
switch trapCause := wstatus.TrapCause(); trapCause {
|
||||
case unix.PTRACE_EVENT_SECCOMP:
|
||||
if ph.execved {
|
||||
// give the customized handle for syscall
|
||||
err := ph.handleTrap(pid)
|
||||
if err != nil {
|
||||
status = runner.StatusDisallowedSyscall
|
||||
errStr = err.Error()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ph.Handler.Debug("ptrace seccomp before execve (should be the execve syscall)")
|
||||
}
|
||||
|
||||
case unix.PTRACE_EVENT_CLONE:
|
||||
ph.Handler.Debug("ptrace stop clone")
|
||||
case unix.PTRACE_EVENT_VFORK:
|
||||
ph.Handler.Debug("ptrace stop vfork")
|
||||
case unix.PTRACE_EVENT_FORK:
|
||||
ph.Handler.Debug("ptrace stop fork")
|
||||
case unix.PTRACE_EVENT_EXEC:
|
||||
// forked tracee have successfully called execve
|
||||
if !ph.execved {
|
||||
ph.fTime = time.Now()
|
||||
ph.execved = true
|
||||
}
|
||||
ph.Handler.Debug("ptrace stop exec")
|
||||
|
||||
default:
|
||||
ph.Handler.Debug("ptrace unexpected trap cause: ", trapCause)
|
||||
}
|
||||
unix.PtraceCont(pid, 0)
|
||||
return
|
||||
|
||||
// check if cpu rlimit hit
|
||||
case unix.SIGXCPU:
|
||||
status = runner.StatusTimeLimitExceeded
|
||||
case unix.SIGXFSZ:
|
||||
status = runner.StatusOutputLimitExceeded
|
||||
}
|
||||
if status != runner.StatusNormal {
|
||||
return
|
||||
}
|
||||
// Likely encountered SIGSEGV (segment violation)
|
||||
// Or compiler child exited
|
||||
if stopSig != unix.SIGSTOP {
|
||||
ph.Handler.Debug("ptrace unexpected stop signal: ", stopSig)
|
||||
}
|
||||
ph.Handler.Debug("ptrace stopped")
|
||||
unix.PtraceCont(pid, int(stopSig))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// handleTrap handles the seccomp trap including the custom handle
|
||||
func (ph *ptraceHandle) handleTrap(pid int) error {
|
||||
ph.Handler.Debug("seccomp traced")
|
||||
// msg, err := unix.PtraceGetEventMsg(pid)
|
||||
// if err != nil {
|
||||
// t.Handler.Debug("PtraceGetEventMsg failed:", err)
|
||||
// return err
|
||||
// }
|
||||
if ph.Handler != nil {
|
||||
ctx, err := getTrapContext(pid)
|
||||
if err != nil {
|
||||
t.Handler.Debug("getTrapContext failed:", err)
|
||||
return err
|
||||
}
|
||||
syscallName, err := t.Handler.GetSyscallName(ctx)
|
||||
t.Handler.Debug("disallowed syscall: ", ctx.SyscallNo(), syscallName, err)
|
||||
return t.Handler.HandlerDisallow(syscallName)
|
||||
act := ph.Handler.Handle(ctx)
|
||||
|
||||
case seccomp.MsgHandle:
|
||||
if t.Handler != nil {
|
||||
ctx, err := getTrapContext(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
act := t.Handler.Handle(ctx)
|
||||
switch act {
|
||||
case TraceBan:
|
||||
// Set the syscallno to -1 and return value into register to skip syscall.
|
||||
// https://www.kernel.org/doc/Documentation/prctl/pkg/seccomp_filter.txt
|
||||
return ctx.skipSyscall()
|
||||
|
||||
switch act {
|
||||
case TraceBan:
|
||||
// Set the syscallno to -1 and return value into register to skip syscall.
|
||||
// https://www.kernel.org/doc/Documentation/prctl/pkg/seccomp_filter.txt
|
||||
return ctx.skipSyscall()
|
||||
|
||||
case TraceKill:
|
||||
return runner.StatusDisallowedSyscall
|
||||
}
|
||||
case TraceKill:
|
||||
return runner.StatusDisallowedSyscall
|
||||
}
|
||||
|
||||
default:
|
||||
// undefined seccomp message, possible set up filter wrong
|
||||
t.Handler.Debug("unknown seccomp trap message: ", msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/criyle/go-sandbox/pkg/seccomp/libseccomp"
|
||||
"github.com/criyle/go-sandbox/ptracer"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
)
|
||||
|
||||
type tracerHandler struct {
|
||||
@ -59,17 +58,15 @@ func (h *tracerHandler) checkStat(ctx *ptracer.Context, addr uint) ptracer.Trace
|
||||
}
|
||||
|
||||
func (h *tracerHandler) Handle(ctx *ptracer.Context) ptracer.TraceAction {
|
||||
var (
|
||||
action ptracer.TraceAction
|
||||
syscallNo = ctx.SyscallNo()
|
||||
syscallName, err = libseccomp.ToSyscallName(syscallNo)
|
||||
)
|
||||
h.Debug("syscall: ", syscallNo, syscallName, err)
|
||||
syscallNo := ctx.SyscallNo()
|
||||
syscallName, err := libseccomp.ToSyscallName(syscallNo)
|
||||
h.Debug("syscall:", syscallNo, syscallName, err)
|
||||
if err != nil {
|
||||
h.Debug("invalid syscall no")
|
||||
return ptracer.TraceKill
|
||||
}
|
||||
|
||||
action := ptracer.TraceKill
|
||||
switch syscallName {
|
||||
case "open":
|
||||
action = h.checkOpen(ctx, ctx.Arg0(), ctx.Arg1())
|
||||
@ -105,8 +102,12 @@ func (h *tracerHandler) Handle(ctx *ptracer.Context) ptracer.TraceAction {
|
||||
action = h.checkWrite(ctx, ctx.Arg0())
|
||||
case "rename":
|
||||
action = h.checkWrite(ctx, ctx.Arg0())
|
||||
|
||||
default:
|
||||
action = h.Handler.CheckSyscall(syscallName)
|
||||
if h.Unsafe && action == ptracer.TraceKill {
|
||||
action = ptracer.TraceBan
|
||||
}
|
||||
}
|
||||
|
||||
switch action {
|
||||
@ -120,18 +121,6 @@ func (h *tracerHandler) Handle(ctx *ptracer.Context) ptracer.TraceAction {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *tracerHandler) GetSyscallName(ctx *ptracer.Context) (string, error) {
|
||||
syscallNo := ctx.SyscallNo()
|
||||
return libseccomp.ToSyscallName(syscallNo)
|
||||
}
|
||||
|
||||
func (h *tracerHandler) HandlerDisallow(name string) error {
|
||||
if !h.Unsafe {
|
||||
return runner.StatusDisallowedSyscall
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func softBanSyscall(ctx *ptracer.Context) ptracer.TraceAction {
|
||||
ctx.SetReturnValue(-int(BanRet))
|
||||
return ptracer.TraceBan
|
||||
|
||||
Loading…
Reference in New Issue
Block a user