From 464c5ac9322fb56b0a4474d962c1182c25f8d4c4 Mon Sep 17 00:00:00 2001 From: criyle Date: Thu, 21 Jan 2021 00:20:57 -0800 Subject: [PATCH] Backwards compatibility for older kernels >= 3.10 --- README.md | 11 +++++++++++ cmd/runprog/main_linux.go | 11 +++++++++-- container/container_exec_linux.go | 2 +- container/environment_linux.go | 1 + container/protocol_linux.go | 7 ++++--- pkg/cgroup/cgroup.go | 3 +-- pkg/forkexec/bench_linux_test.go | 2 +- pkg/forkexec/fork_child_linux.go | 18 ++++++------------ 8 files changed, 34 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 7944002..3335ef1 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,17 @@ type Environment interface { - config/config.go: all configs toward running specs (similar to UOJ) +## Kernel Versions + +- 4.14: SECCOMP_RET_KILL_PROCESS +- 4.6: CLONE_NEWCGROUP +- 3.19: execveat() +- 3.17: seccomp, memfd_create +- 3.10: CentOS 7 +- 3.8: CLONE_NEWUSER without CAP_SYS_ADMIN, CAP_SETUID, CAP_SETGID +- 3.5: prctl(PR_SET_NO_NEW_PRIVS) +- 2.6.36: prlimit64 + ## Benchmarks ### ForkExec diff --git a/cmd/runprog/main_linux.go b/cmd/runprog/main_linux.go index b281ebf..3a812dc 100644 --- a/cmd/runprog/main_linux.go +++ b/cmd/runprog/main_linux.go @@ -25,11 +25,12 @@ import ( "github.com/criyle/go-sandbox/runner/ptrace" "github.com/criyle/go-sandbox/runner/ptrace/filehandler" "github.com/criyle/go-sandbox/runner/unshare" + "golang.org/x/sys/unix" ) var ( addReadable, addWritable, addRawReadable, addRawWritable arrayFlags - allowProc, unsafe, showDetails, useCGroup, memfile, cred bool + allowProc, unsafe, showDetails, useCGroup, memfile, cred, nucg bool timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64 inputFileName, outputFileName, errorFileName, workPath, runt string @@ -66,6 +67,7 @@ func main() { flag.BoolVar(&memfile, "memfd", false, "Use memfd as exec file") flag.StringVar(&runt, "runner", "ptrace", "Runner for the program (ptrace, ns, container)") flag.BoolVar(&cred, "cred", false, "Generate credential for containers (uid=10000)") + flag.BoolVar(&nucg, "nucg", false, "don't unshare cgroup") flag.Parse() args = flag.Args() @@ -283,12 +285,17 @@ func start() (*runner.Result, error) { stderr = os.Stderr } + cloneFlag := forkexec.UnshareFlags + if nucg { + cloneFlag &= ^unix.CLONE_NEWCGROUP + } + b := container.Builder{ Root: root, Mounts: mb.Mounts, Stderr: stderr, CredGenerator: credG, - CloneFlags: forkexec.UnshareFlags, + CloneFlags: uintptr(cloneFlag), } m, err := b.Build() diff --git a/container/container_exec_linux.go b/container/container_exec_linux.go index 67c70d1..e8e555f 100644 --- a/container/container_exec_linux.go +++ b/container/container_exec_linux.go @@ -84,7 +84,7 @@ func (c *containerServer) handleExecve(cmd *execCmd, msg *unixsocket.Msg) error CTTY: cmd.CTTY, Seccomp: seccomp, - UnshareCgroupAfterSync: true, + UnshareCgroupAfterSync: c.UnshareCgroup, } // starts the runner, error is handled same as wait4 to make communication equal pid, err := r.Start() diff --git a/container/environment_linux.go b/container/environment_linux.go index 1d87761..4b3625d 100644 --- a/container/environment_linux.go +++ b/container/environment_linux.go @@ -129,6 +129,7 @@ func (b *Builder) Build() (Environment, error) { Cred: b.CredGenerator != nil, ContainerUID: b.ContainerUID, ContainerGID: b.ContainerGID, + UnshareCgroup: b.CloneFlags&unix.CLONE_NEWCGROUP == unix.CLONE_NEWCGROUP, }); err != nil { c.Destroy() return nil, err diff --git a/container/protocol_linux.go b/container/protocol_linux.go index c088f84..759ed87 100644 --- a/container/protocol_linux.go +++ b/container/protocol_linux.go @@ -58,9 +58,10 @@ type containerConfig struct { ContainerRoot string Mounts []mount.Mount - Cred bool - ContainerUID int - ContainerGID int + ContainerUID int + ContainerGID int + Cred bool + UnshareCgroup bool } // reply is the reply message send back to controller diff --git a/pkg/cgroup/cgroup.go b/pkg/cgroup/cgroup.go index 53ef7f7..3367511 100644 --- a/pkg/cgroup/cgroup.go +++ b/pkg/cgroup/cgroup.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" ) // Cgroup is the combination of sub-cgroups @@ -198,7 +197,7 @@ func copyCgroupPropertyFromParent(path, name string) error { if err != nil { return err } - if len(strings.TrimSpace(string(b))) > 0 { + if len(bytes.TrimSpace(b)) > 0 { return nil } // otherwise copy from parent, first to ensure it is empty by recurssion diff --git a/pkg/forkexec/bench_linux_test.go b/pkg/forkexec/bench_linux_test.go index 453ce97..28adb68 100644 --- a/pkg/forkexec/bench_linux_test.go +++ b/pkg/forkexec/bench_linux_test.go @@ -157,7 +157,7 @@ func benchmarkRun(r *Runner, b *testing.B) { for pb.Next() { pid, err := r.Start() if err != nil { - b.Fail() + b.Fatal(err) } wait4(pid, b) } diff --git a/pkg/forkexec/fork_child_linux.go b/pkg/forkexec/fork_child_linux.go index adf8132..96cc87e 100644 --- a/pkg/forkexec/fork_child_linux.go +++ b/pkg/forkexec/fork_child_linux.go @@ -282,14 +282,11 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host // if execfile fd is specified, call fexecve if r.ExecFile > 0 { _, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, r.ExecFile, - uintptr(unsafe.Pointer(&empty[0])), - uintptr(unsafe.Pointer(&argv[0])), + uintptr(unsafe.Pointer(&empty[0])), uintptr(unsafe.Pointer(&argv[0])), uintptr(unsafe.Pointer(&env[0])), unix.AT_EMPTY_PATH, 0) } else { - _, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, uintptr(_AT_FDCWD), - uintptr(unsafe.Pointer(argv0)), - uintptr(unsafe.Pointer(&argv[0])), - uintptr(unsafe.Pointer(&env[0])), 0, 0) + _, _, err1 = syscall.RawSyscall(unix.SYS_EXECVE, uintptr(unsafe.Pointer(argv0)), + uintptr(unsafe.Pointer(&argv[0])), uintptr(unsafe.Pointer(&env[0]))) } // Fix potential ETXTBSY but with caution (max 50 attempt) // The ETXTBSY happens when we copy the executable into container, another goroutine @@ -304,14 +301,11 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host syscall.RawSyscall(unix.SYS_NANOSLEEP, uintptr(unsafe.Pointer(&etxtbsyRetryInterval)), 0, 0) if r.ExecFile > 0 { _, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, r.ExecFile, - uintptr(unsafe.Pointer(&empty[0])), - uintptr(unsafe.Pointer(&argv[0])), + uintptr(unsafe.Pointer(&empty[0])), uintptr(unsafe.Pointer(&argv[0])), uintptr(unsafe.Pointer(&env[0])), unix.AT_EMPTY_PATH, 0) } else { - _, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, uintptr(_AT_FDCWD), - uintptr(unsafe.Pointer(argv0)), - uintptr(unsafe.Pointer(&argv[0])), - uintptr(unsafe.Pointer(&env[0])), 0, 0) + _, _, err1 = syscall.RawSyscall(unix.SYS_EXECVE, uintptr(unsafe.Pointer(argv0)), + uintptr(unsafe.Pointer(&argv[0])), uintptr(unsafe.Pointer(&env[0]))) } }