mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
add benchmark to forkexec / seccomp
This commit is contained in:
parent
b8c180569f
commit
67c0621b7f
11
README.md
11
README.md
@ -53,6 +53,17 @@ Configuations:
|
|||||||
|
|
||||||
- run_program/config.go: all configs toward running specs
|
- run_program/config.go: all configs toward running specs
|
||||||
|
|
||||||
|
Benchmarks for unshare:
|
||||||
|
|
||||||
|
- 1ms: fork, unshare pid / user / cgroup
|
||||||
|
- 50ms: unshare ipc / mount
|
||||||
|
- 100ms: unshare pid & user & cgroup & mount & pivot root
|
||||||
|
- 400ms: unshare net
|
||||||
|
- 800ms: unshare all
|
||||||
|
- 880ms: unshare all & pivot root
|
||||||
|
|
||||||
|
It seems unshare net or ipc takes time, maybe limits action by seccomp instead.
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
1. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4 rusage)
|
1. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4 rusage)
|
||||||
|
|||||||
188
forkexec/bench_test.go
Normal file
188
forkexec/bench_test.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package forkexec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/types/mount"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// All testing data were from docker env on amd64 arch
|
||||||
|
|
||||||
|
const (
|
||||||
|
roBind = unix.MS_BIND | unix.MS_NOSUID | unix.MS_PRIVATE | unix.MS_RDONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultBind = []string{"/usr", "/lib", "/lib64", "/bin"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// BenchmarkSimpleFork is about 0.70ms/op
|
||||||
|
func BenchmarkSimpleFork(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnsharePid is about 0.79ms/op
|
||||||
|
func BenchmarkUnsharePid(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWPID
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareUser is about 0.84ms/op
|
||||||
|
func BenchmarkUnshareUser(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWUSER
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareUts is about 0.78ms/op
|
||||||
|
func BenchmarkUnshareUts(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWUTS
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareCgroup is about 0.85ms/op
|
||||||
|
func BenchmarkUnshareCgroup(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWCGROUP
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareIpc is about 51ms/op
|
||||||
|
func BenchmarkUnshareIpc(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWIPC
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareMount is about 51ms/op
|
||||||
|
func BenchmarkUnshareMount(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWNS
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareNet is about 426ms/op
|
||||||
|
func BenchmarkUnshareNet(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWNET
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkFastUnshareMountPivot is about 104ms/op
|
||||||
|
func BenchmarkFastUnshareMountPivot(b *testing.B) {
|
||||||
|
root, err := ioutil.TempDir("", "ns")
|
||||||
|
if err != nil {
|
||||||
|
b.Errorf("failed to create temp dir")
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(root)
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = unix.CLONE_NEWNS | unix.CLONE_NEWPID | unix.CLONE_NEWUSER | unix.CLONE_NEWUTS | unix.CLONE_NEWCGROUP
|
||||||
|
r.PivotRoot = root
|
||||||
|
r.NoNewPrivs = true
|
||||||
|
r.DropCaps = true
|
||||||
|
r.Mounts = getMounts(defaultBind)
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareAll is about 800ms/op
|
||||||
|
func BenchmarkUnshareAll(b *testing.B) {
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = UnshareFlags
|
||||||
|
r.NoNewPrivs = true
|
||||||
|
r.DropCaps = true
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkUnshareMountPivot is about 880ms/op
|
||||||
|
func BenchmarkUnshareMountPivot(b *testing.B) {
|
||||||
|
root, err := ioutil.TempDir("", "ns")
|
||||||
|
if err != nil {
|
||||||
|
b.Errorf("failed to create temp dir")
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(root)
|
||||||
|
r, f := getRunner(b)
|
||||||
|
defer f.Close()
|
||||||
|
r.UnshareFlags = UnshareFlags
|
||||||
|
r.PivotRoot = root
|
||||||
|
r.NoNewPrivs = true
|
||||||
|
r.DropCaps = true
|
||||||
|
r.Mounts = getMounts(defaultBind)
|
||||||
|
benchmarkRun(r, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRunner(b *testing.B) (*Runner, *os.File) {
|
||||||
|
f := openNull(b)
|
||||||
|
return &Runner{
|
||||||
|
Args: []string{"/bin/echo"},
|
||||||
|
Env: []string{"PATH=/bin"},
|
||||||
|
Files: []uintptr{f.Fd(), f.Fd(), f.Fd()},
|
||||||
|
WorkDir: "/bin",
|
||||||
|
}, f
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkRun(r *Runner, b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
pid, err := r.Start()
|
||||||
|
if err != nil {
|
||||||
|
b.Fail()
|
||||||
|
}
|
||||||
|
wait4(pid, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMounts(dirs []string) []*mount.Mount {
|
||||||
|
ret := make([]*mount.Mount, 0, len(dirs))
|
||||||
|
for _, d := range dirs {
|
||||||
|
if _, err := os.Stat(d); !os.IsNotExist(err) {
|
||||||
|
ret = append(ret, getMount(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMount(dir string) *mount.Mount {
|
||||||
|
return &mount.Mount{
|
||||||
|
Source: dir,
|
||||||
|
Target: dir[1:],
|
||||||
|
Flags: roBind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func openNull(b *testing.B) *os.File {
|
||||||
|
f, err := os.OpenFile("/dev/null", os.O_RDWR, 0666)
|
||||||
|
if err != nil {
|
||||||
|
b.Errorf("Failed to open %v", err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func wait4(pid int, b *testing.B) {
|
||||||
|
var wstat syscall.WaitStatus
|
||||||
|
for {
|
||||||
|
syscall.Wait4(pid, &wstat, 0, nil)
|
||||||
|
if wstat.Exited() {
|
||||||
|
if s := wstat.ExitStatus(); s != 0 {
|
||||||
|
b.Errorf("Exited: %d", s)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@ -4,5 +4,5 @@ go 1.12
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1
|
github.com/seccomp/libseccomp-golang v0.9.1
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -2,3 +2,5 @@ github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46r
|
|||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
|
||||||
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|||||||
@ -6,6 +6,20 @@ import (
|
|||||||
libseccomp "github.com/seccomp/libseccomp-golang"
|
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultSyscallAllows = []string{
|
||||||
|
"read", "write", "readv", "writev", "close", "fstat", "lseek", "dup", "dup2", "dup3", "ioctl", "fcntl", "fadvise64",
|
||||||
|
"mmap", "mprotect", "munmap", "brk", "mremap", "msync", "mincore", "madvise",
|
||||||
|
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn", "rt_sigpending", "sigaltstack",
|
||||||
|
"getcwd", "exit", "exit_group", "arch_prctl",
|
||||||
|
"gettimeofday", "getrlimit", "getrusage", "times", "time", "clock_gettime", "restart_syscall",
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultSyscallTraces = []string{
|
||||||
|
"execve", "open", "openat", "unlink", "unlinkat", "readlink", "readlinkat", "lstat", "stat", "access", "faccessat",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func TestBuildFilter(t *testing.T) {
|
func TestBuildFilter(t *testing.T) {
|
||||||
defaultAction := libseccomp.ActKill
|
defaultAction := libseccomp.ActKill
|
||||||
filter, err := buildFilterMock(defaultAction)
|
filter, err := buildFilterMock(defaultAction)
|
||||||
@ -64,6 +78,15 @@ func TestAddActionFail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BenchmarkBuildDefaultFilter is about 0.2ms/op
|
||||||
|
func BenchmarkBuildDefaultFilter(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
filter, _ := BuildFilter(libseccomp.ActKill, libseccomp.ActTrace, defaultSyscallAllows, defaultSyscallTraces)
|
||||||
|
_, _ = FilterToBPF(filter)
|
||||||
|
filter.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func buildFilterMock(d libseccomp.ScmpAction) (*libseccomp.ScmpFilter, error) {
|
func buildFilterMock(d libseccomp.ScmpAction) (*libseccomp.ScmpFilter, error) {
|
||||||
defaultTrace := libseccomp.ActTrace
|
defaultTrace := libseccomp.ActTrace
|
||||||
allow := []string{"fork"}
|
allow := []string{"fork"}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user