Backwards compatibility for older kernels >= 3.10

This commit is contained in:
criyle 2021-01-21 00:20:57 -08:00
parent 29aefc839b
commit 464c5ac932
8 changed files with 34 additions and 21 deletions

View File

@ -179,6 +179,17 @@ type Environment interface {
- config/config.go: all configs toward running specs (similar to UOJ) - 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 ## Benchmarks
### ForkExec ### ForkExec

View File

@ -25,11 +25,12 @@ import (
"github.com/criyle/go-sandbox/runner/ptrace" "github.com/criyle/go-sandbox/runner/ptrace"
"github.com/criyle/go-sandbox/runner/ptrace/filehandler" "github.com/criyle/go-sandbox/runner/ptrace/filehandler"
"github.com/criyle/go-sandbox/runner/unshare" "github.com/criyle/go-sandbox/runner/unshare"
"golang.org/x/sys/unix"
) )
var ( var (
addReadable, addWritable, addRawReadable, addRawWritable arrayFlags 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 timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
inputFileName, outputFileName, errorFileName, workPath, runt string inputFileName, outputFileName, errorFileName, workPath, runt string
@ -66,6 +67,7 @@ func main() {
flag.BoolVar(&memfile, "memfd", false, "Use memfd as exec file") flag.BoolVar(&memfile, "memfd", false, "Use memfd as exec file")
flag.StringVar(&runt, "runner", "ptrace", "Runner for the program (ptrace, ns, container)") 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(&cred, "cred", false, "Generate credential for containers (uid=10000)")
flag.BoolVar(&nucg, "nucg", false, "don't unshare cgroup")
flag.Parse() flag.Parse()
args = flag.Args() args = flag.Args()
@ -283,12 +285,17 @@ func start() (*runner.Result, error) {
stderr = os.Stderr stderr = os.Stderr
} }
cloneFlag := forkexec.UnshareFlags
if nucg {
cloneFlag &= ^unix.CLONE_NEWCGROUP
}
b := container.Builder{ b := container.Builder{
Root: root, Root: root,
Mounts: mb.Mounts, Mounts: mb.Mounts,
Stderr: stderr, Stderr: stderr,
CredGenerator: credG, CredGenerator: credG,
CloneFlags: forkexec.UnshareFlags, CloneFlags: uintptr(cloneFlag),
} }
m, err := b.Build() m, err := b.Build()

View File

@ -84,7 +84,7 @@ func (c *containerServer) handleExecve(cmd *execCmd, msg *unixsocket.Msg) error
CTTY: cmd.CTTY, CTTY: cmd.CTTY,
Seccomp: seccomp, Seccomp: seccomp,
UnshareCgroupAfterSync: true, UnshareCgroupAfterSync: c.UnshareCgroup,
} }
// starts the runner, error is handled same as wait4 to make communication equal // starts the runner, error is handled same as wait4 to make communication equal
pid, err := r.Start() pid, err := r.Start()

View File

@ -129,6 +129,7 @@ func (b *Builder) Build() (Environment, error) {
Cred: b.CredGenerator != nil, Cred: b.CredGenerator != nil,
ContainerUID: b.ContainerUID, ContainerUID: b.ContainerUID,
ContainerGID: b.ContainerGID, ContainerGID: b.ContainerGID,
UnshareCgroup: b.CloneFlags&unix.CLONE_NEWCGROUP == unix.CLONE_NEWCGROUP,
}); err != nil { }); err != nil {
c.Destroy() c.Destroy()
return nil, err return nil, err

View File

@ -58,9 +58,10 @@ type containerConfig struct {
ContainerRoot string ContainerRoot string
Mounts []mount.Mount Mounts []mount.Mount
Cred bool ContainerUID int
ContainerUID int ContainerGID int
ContainerGID int Cred bool
UnshareCgroup bool
} }
// reply is the reply message send back to controller // reply is the reply message send back to controller

View File

@ -6,7 +6,6 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
// Cgroup is the combination of sub-cgroups // Cgroup is the combination of sub-cgroups
@ -198,7 +197,7 @@ func copyCgroupPropertyFromParent(path, name string) error {
if err != nil { if err != nil {
return err return err
} }
if len(strings.TrimSpace(string(b))) > 0 { if len(bytes.TrimSpace(b)) > 0 {
return nil return nil
} }
// otherwise copy from parent, first to ensure it is empty by recurssion // otherwise copy from parent, first to ensure it is empty by recurssion

View File

@ -157,7 +157,7 @@ func benchmarkRun(r *Runner, b *testing.B) {
for pb.Next() { for pb.Next() {
pid, err := r.Start() pid, err := r.Start()
if err != nil { if err != nil {
b.Fail() b.Fatal(err)
} }
wait4(pid, b) wait4(pid, b)
} }

View File

@ -282,14 +282,11 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
// if execfile fd is specified, call fexecve // if execfile fd is specified, call fexecve
if r.ExecFile > 0 { if r.ExecFile > 0 {
_, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, r.ExecFile, _, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, r.ExecFile,
uintptr(unsafe.Pointer(&empty[0])), uintptr(unsafe.Pointer(&empty[0])), uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&env[0])), unix.AT_EMPTY_PATH, 0) uintptr(unsafe.Pointer(&env[0])), unix.AT_EMPTY_PATH, 0)
} else { } else {
_, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, uintptr(_AT_FDCWD), _, _, err1 = syscall.RawSyscall(unix.SYS_EXECVE, uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(argv0)), uintptr(unsafe.Pointer(&argv[0])), uintptr(unsafe.Pointer(&env[0])))
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&env[0])), 0, 0)
} }
// Fix potential ETXTBSY but with caution (max 50 attempt) // Fix potential ETXTBSY but with caution (max 50 attempt)
// The ETXTBSY happens when we copy the executable into container, another goroutine // 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) syscall.RawSyscall(unix.SYS_NANOSLEEP, uintptr(unsafe.Pointer(&etxtbsyRetryInterval)), 0, 0)
if r.ExecFile > 0 { if r.ExecFile > 0 {
_, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, r.ExecFile, _, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, r.ExecFile,
uintptr(unsafe.Pointer(&empty[0])), uintptr(unsafe.Pointer(&empty[0])), uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&env[0])), unix.AT_EMPTY_PATH, 0) uintptr(unsafe.Pointer(&env[0])), unix.AT_EMPTY_PATH, 0)
} else { } else {
_, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, uintptr(_AT_FDCWD), _, _, err1 = syscall.RawSyscall(unix.SYS_EXECVE, uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(argv0)), uintptr(unsafe.Pointer(&argv[0])), uintptr(unsafe.Pointer(&env[0])))
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&env[0])), 0, 0)
} }
} }