mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-09-26 23:19:11 +08:00
forkexec: report error location
This commit is contained in:
parent
26c5f63dcb
commit
4e55516ff3
4
go.mod
4
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/elastic/go-seccomp-bpf v1.1.0
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
8
go.sum
8
go.sum
@ -15,14 +15,14 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
96
pkg/forkexec/errloc_linux.go
Normal file
96
pkg/forkexec/errloc_linux.go
Normal file
@ -0,0 +1,96 @@
|
||||
package forkexec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type ErrorLocation int
|
||||
|
||||
type ChildError struct {
|
||||
Err syscall.Errno
|
||||
Location ErrorLocation
|
||||
Index int
|
||||
}
|
||||
|
||||
const (
|
||||
LocClone ErrorLocation = iota + 1
|
||||
LocCloseWrite
|
||||
LocUnshareUserRead
|
||||
LocGetPid
|
||||
LocKeepCapability
|
||||
LocSetGroups
|
||||
LocSetGid
|
||||
LocSetUid
|
||||
LocDup3
|
||||
LocFcntl
|
||||
LocSetSid
|
||||
LocIoctl
|
||||
LocMountRoot
|
||||
LocMountTmpfs
|
||||
LocMountChdir
|
||||
LocMount
|
||||
LocMountMkdir
|
||||
LocPivotRoot
|
||||
LocUmount
|
||||
LocUnlink
|
||||
LocMountRootReadonly
|
||||
LocChdir
|
||||
LocSetRlimit
|
||||
LocSetNoNewPrivs
|
||||
LocDropCapability
|
||||
LocSetCap
|
||||
LocPtraceMe
|
||||
LocStop
|
||||
LocSeccomp
|
||||
LocSyncWrite
|
||||
LocSyncRead
|
||||
LocExecve
|
||||
)
|
||||
|
||||
var locToString = []string{
|
||||
"unknown",
|
||||
"clone",
|
||||
"close_write",
|
||||
"unshare_user_read",
|
||||
"getpid",
|
||||
"keep_capability",
|
||||
"setgroups",
|
||||
"setgid",
|
||||
"setuid",
|
||||
"dup3",
|
||||
"fcntl",
|
||||
"setsid",
|
||||
"ioctl",
|
||||
"mount(root)",
|
||||
"mount(tmpfs)",
|
||||
"mount(chdir)",
|
||||
"mount",
|
||||
"mount(mkdir)",
|
||||
"pivot_root",
|
||||
"umount",
|
||||
"unlink",
|
||||
"mount(readonly)",
|
||||
"chdir",
|
||||
"setrlimt",
|
||||
"set_no_new_privs",
|
||||
"drop_capability",
|
||||
"set_cap",
|
||||
"ptrace_me",
|
||||
"stop",
|
||||
"seccomp",
|
||||
"sync_write",
|
||||
"sync_read",
|
||||
"execve",
|
||||
}
|
||||
|
||||
func (e ErrorLocation) String() string {
|
||||
if e >= LocClone && e <= LocExecve {
|
||||
return locToString[e]
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func (e ChildError) Error() string {
|
||||
return fmt.Sprintf("%s(%d): %s", e.Location.String(), e.Index, e.Err.Error())
|
||||
}
|
@ -11,14 +11,13 @@ import (
|
||||
//go:norace
|
||||
func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, hostname, domainname, pivotRoot *byte, p [2]int) (r1 uintptr, err1 syscall.Errno) {
|
||||
var (
|
||||
pid uintptr
|
||||
err2 syscall.Errno
|
||||
unshareUser = r.CloneFlags&unix.CLONE_NEWUSER == unix.CLONE_NEWUSER
|
||||
loc ErrorLocation
|
||||
idx, pipe int
|
||||
childError ChildError
|
||||
)
|
||||
|
||||
// similar to exec_linux, avoid side effect by shuffling around
|
||||
fd, nextfd := prepareFds(r.Files)
|
||||
pipe := p[1]
|
||||
|
||||
// Acquire the fork lock so that no other threads
|
||||
// create new fds that are not yet close-on-exec
|
||||
@ -40,9 +39,33 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
afterForkInChild()
|
||||
// Notice: cannot call any GO functions beyond this point
|
||||
|
||||
pipe, loc, idx, err1 = forkAndExecInChild1(r, argv0, argv, env, workdir, hostname, domainname, pivotRoot, fd, nextfd, p)
|
||||
|
||||
// send error code on pipe
|
||||
childError.Err = err1
|
||||
childError.Location = loc
|
||||
childError.Index = idx
|
||||
|
||||
// send error code on pipe
|
||||
syscall.RawSyscall(unix.SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&childError)), unsafe.Sizeof(childError))
|
||||
for {
|
||||
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(err1), 0, 0)
|
||||
}
|
||||
// cannot reach this point
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func forkAndExecInChild1(r *Runner, argv0 *byte, argv, env []*byte, workdir, hostname, domainname, pivotRoot *byte, fd []int, nextfd int, p [2]int) (pipe int, loc ErrorLocation, idx int, err1 syscall.Errno) {
|
||||
pipe = p[1]
|
||||
var (
|
||||
r1, pid uintptr
|
||||
err2 syscall.Errno
|
||||
unshareUser = r.CloneFlags&unix.CLONE_NEWUSER == unix.CLONE_NEWUSER
|
||||
)
|
||||
|
||||
// Close write end of pipe
|
||||
if _, _, err1 = syscall.RawSyscall(syscall.SYS_CLOSE, uintptr(p[0]), 0, 0); err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocCloseWrite, 0, err1
|
||||
}
|
||||
|
||||
// If usernamespace is unshared, uid map and gid map is required to create folders
|
||||
@ -53,22 +76,22 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
if unshareUser {
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocUnshareUserRead, 0, err1
|
||||
}
|
||||
if r1 != unsafe.Sizeof(err2) {
|
||||
err1 = syscall.EINVAL
|
||||
goto childerror
|
||||
return pipe, LocUnshareUserRead, 0, err1
|
||||
}
|
||||
if err2 != 0 {
|
||||
err1 = err2
|
||||
goto childerror
|
||||
return pipe, LocUnshareUserRead, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
// Get pid of child
|
||||
pid, _, err1 = syscall.RawSyscall(syscall.SYS_GETPID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocGetPid, 0, err1
|
||||
}
|
||||
|
||||
// keep capabilities through set_uid / set_gid calls (make sure we can use unshare cgroup), later dropped
|
||||
@ -76,7 +99,7 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECUREBITS,
|
||||
_SECURE_KEEP_CAPS_LOCKED|_SECURE_NO_SETUID_FIXUP|_SECURE_NO_SETUID_FIXUP_LOCKED, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocKeepCapability, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,16 +113,16 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
if !(r.GIDMappings != nil && !r.GIDMappingsEnableSetgroups && ngroups == 0) && !cred.NoSetGroups {
|
||||
_, _, err1 = syscall.RawSyscall(unix.SYS_SETGROUPS, ngroups, groups, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSetGroups, 0, err1
|
||||
}
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(unix.SYS_SETGID, uintptr(cred.Gid), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSetGid, 0, err1
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(unix.SYS_SETUID, uintptr(cred.Uid), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSetUid, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +131,7 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
if pipe < nextfd {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(pipe), uintptr(nextfd), syscall.O_CLOEXEC)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocDup3, 0, err1
|
||||
}
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
@ -116,7 +139,7 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
if r.ExecFile > 0 && int(r.ExecFile) < nextfd {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, r.ExecFile, uintptr(nextfd), syscall.O_CLOEXEC)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocDup3, 0, err1
|
||||
}
|
||||
r.ExecFile = uintptr(nextfd)
|
||||
nextfd++
|
||||
@ -129,7 +152,7 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), syscall.O_CLOEXEC)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocDup3, 0, err1
|
||||
}
|
||||
// Set up close on exec
|
||||
fd[i] = nextfd
|
||||
@ -146,43 +169,45 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
// dup2(i, i) will not clear close on exec flag, need to reset the flag
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_FCNTL, uintptr(fd[i]), syscall.F_SETFD, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocFcntl, 0, err1
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(fd[i]), uintptr(i), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocDup3, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
// Set the session ID
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_SETSID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSetSid, 0, err1
|
||||
}
|
||||
|
||||
// Set the controlling TTY
|
||||
if r.CTTY {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCSCTTY), 1)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocIoctl, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
if err1 = pivotAndMount(r, pivotRoot); err1 != 0 {
|
||||
goto childerror
|
||||
// Mount file system
|
||||
loc, idx, err1 = pivotAndMount(r, pivotRoot)
|
||||
if err1 != 0 {
|
||||
return pipe, loc, idx, err1
|
||||
}
|
||||
|
||||
// SetHostName
|
||||
if hostname != nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_SETHOSTNAME,
|
||||
syscall.RawSyscall(syscall.SYS_SETHOSTNAME,
|
||||
uintptr(unsafe.Pointer(hostname)), uintptr(len(r.HostName)), 0)
|
||||
}
|
||||
|
||||
// SetDomainName
|
||||
if domainname != nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_SETDOMAINNAME,
|
||||
syscall.RawSyscall(syscall.SYS_SETDOMAINNAME,
|
||||
uintptr(unsafe.Pointer(domainname)), uintptr(len(r.DomainName)), 0)
|
||||
}
|
||||
|
||||
@ -190,16 +215,16 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
if workdir != nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_CHDIR, uintptr(unsafe.Pointer(workdir)), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocChdir, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
// Set limit
|
||||
for _, rlim := range r.RLimits {
|
||||
for i, rlim := range r.RLimits {
|
||||
// prlimit instead of setrlimit to avoid 32-bit limitation (linux > 3.2)
|
||||
_, _, err1 = syscall.RawSyscall6(syscall.SYS_PRLIMIT64, 0, uintptr(rlim.Res), uintptr(unsafe.Pointer(&rlim.Rlim)), 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSetRlimit, i, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,7 +232,7 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
if r.NoNewPrivs || r.Seccomp != nil {
|
||||
_, _, err1 = syscall.RawSyscall6(syscall.SYS_PRCTL, unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSetNoNewPrivs, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,22 +242,23 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECUREBITS,
|
||||
_SECURE_KEEP_CAPS_LOCKED|_SECURE_NO_SETUID_FIXUP|_SECURE_NO_SETUID_FIXUP_LOCKED|_SECURE_NOROOT|_SECURE_NOROOT_LOCKED, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocDropCapability, 0, err1
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(&dropCapHeader)), uintptr(unsafe.Pointer(&dropCapData)), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSetCap, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
// Enable Ptrace & sync with parent (since ptrace_me is a blocking operation)
|
||||
if r.Ptrace && r.Seccomp != nil {
|
||||
if err1 = syncAndDropCaps(r, pipe); err1 != 0 {
|
||||
goto childerror
|
||||
loc, err1 = syncAndDropCaps(r, pipe)
|
||||
if err1 != 0 {
|
||||
return pipe, loc, 0, err1
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PTRACE, uintptr(syscall.PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocPtraceMe, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,7 +269,7 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
// Stop to wait for ptrace tracer
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_KILL, pid, uintptr(syscall.SIGSTOP), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocStop, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,14 +283,15 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
// Load seccomp filter
|
||||
_, _, err1 = syscall.RawSyscall(unix.SYS_SECCOMP, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, uintptr(unsafe.Pointer(r.Seccomp)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocSeccomp, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
// Before exec, sync with parent through pipe (configured as close_on_exec)
|
||||
if !r.Ptrace || r.Seccomp == nil {
|
||||
if err1 = syncAndDropCaps(r, pipe); err1 != 0 {
|
||||
goto childerror
|
||||
loc, err1 = syncAndDropCaps(r, pipe)
|
||||
if err1 != 0 {
|
||||
return pipe, loc, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +299,7 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
if r.Ptrace && r.Seccomp == nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PTRACE, uintptr(syscall.PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
return pipe, LocPtraceMe, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,58 +335,50 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
|
||||
uintptr(unsafe.Pointer(&argv[0])), uintptr(unsafe.Pointer(&env[0])))
|
||||
}
|
||||
}
|
||||
|
||||
childerror:
|
||||
// send error code on pipe
|
||||
syscall.RawSyscall(unix.SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
|
||||
for {
|
||||
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(err1+err2), 0, 0)
|
||||
}
|
||||
// cannot reach this point
|
||||
return pipe, LocExecve, 0, err1
|
||||
}
|
||||
|
||||
// syncAndDropCaps sync with parent process & drop capabilities afterwards
|
||||
// nosplit ensure there is no stack growth after fork error
|
||||
//go:nosplit
|
||||
func syncAndDropCaps(r *Runner, pipe int) (err1 syscall.Errno) {
|
||||
func syncAndDropCaps(r *Runner, pipe int) (loc ErrorLocation, err1 syscall.Errno) {
|
||||
var (
|
||||
r1 uintptr
|
||||
err2 syscall.Errno
|
||||
)
|
||||
err2 = 0
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
if r1 == 0 || err1 != 0 {
|
||||
return
|
||||
return LocSyncWrite, err1
|
||||
}
|
||||
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
if r1 == 0 || err1 != 0 {
|
||||
return
|
||||
return LocSyncRead, err1
|
||||
}
|
||||
|
||||
// unshare cgroup namespace
|
||||
if r.UnshareCgroupAfterSync {
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_UNSHARE, uintptr(unix.CLONE_NEWCGROUP), 0, 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
}
|
||||
// do not error if unshare fails, it is not critical
|
||||
syscall.RawSyscall(syscall.SYS_UNSHARE, uintptr(unix.CLONE_NEWCGROUP), 0, 0)
|
||||
|
||||
if r.DropCaps || r.Credential != nil {
|
||||
// make sure the children have no privilege at all
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECUREBITS,
|
||||
_SECURE_KEEP_CAPS_LOCKED|_SECURE_NO_SETUID_FIXUP|_SECURE_NO_SETUID_FIXUP_LOCKED|_SECURE_NOROOT|_SECURE_NOROOT_LOCKED, 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocKeepCapability, err1
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(&dropCapHeader)), uintptr(unsafe.Pointer(&dropCapData)), 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocSetCap, err1
|
||||
}
|
||||
}
|
||||
|
||||
if r.Seccomp != nil {
|
||||
// Load seccomp filter
|
||||
_, _, err1 = syscall.RawSyscall(unix.SYS_SECCOMP, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, uintptr(unsafe.Pointer(r.Seccomp)))
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocSeccomp, err1
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -368,14 +387,15 @@ func syncAndDropCaps(r *Runner, pipe int) (err1 syscall.Errno) {
|
||||
|
||||
// pivotAndMount performs pivot & mount in child process
|
||||
//go:nosplit
|
||||
func pivotAndMount(r *Runner, pivotRoot *byte) (err1 syscall.Errno) {
|
||||
func pivotAndMount(r *Runner, pivotRoot *byte) (loc ErrorLocation, idx int, err1 syscall.Errno) {
|
||||
loc = LocMount
|
||||
// If mount point is unshared, mark root as private to avoid propagate
|
||||
// outside to the original mount namespace
|
||||
if r.CloneFlags&syscall.CLONE_NEWNS == syscall.CLONE_NEWNS {
|
||||
_, _, err1 = syscall.RawSyscall6(syscall.SYS_MOUNT, uintptr(unsafe.Pointer(&none[0])),
|
||||
uintptr(unsafe.Pointer(&slash[0])), 0, syscall.MS_REC|syscall.MS_PRIVATE, 0, 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocMountRoot, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,30 +406,30 @@ func pivotAndMount(r *Runner, pivotRoot *byte) (err1 syscall.Errno) {
|
||||
uintptr(unsafe.Pointer(pivotRoot)), uintptr(unsafe.Pointer(&tmpfs[0])), 0,
|
||||
uintptr(unsafe.Pointer(&empty[0])), 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocMountTmpfs, 0, err1
|
||||
}
|
||||
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_CHDIR, uintptr(unsafe.Pointer(pivotRoot)), 0, 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocMountChdir, 0, err1
|
||||
}
|
||||
}
|
||||
|
||||
// performing mounts
|
||||
for _, m := range r.Mounts {
|
||||
for i, m := range r.Mounts {
|
||||
// mkdirs(target)
|
||||
for i, p := range m.Prefixes {
|
||||
// if target mount point is a file, mknod(target)
|
||||
if i == len(m.Prefixes)-1 && m.MakeNod {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_MKNODAT, uintptr(_AT_FDCWD), uintptr(unsafe.Pointer(p)), 0755)
|
||||
if err1 != 0 && err1 != syscall.EEXIST {
|
||||
return
|
||||
return LocMountMkdir, 0, err1
|
||||
}
|
||||
break
|
||||
}
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_MKDIRAT, uintptr(_AT_FDCWD), uintptr(unsafe.Pointer(p)), 0755)
|
||||
if err1 != 0 && err1 != syscall.EEXIST {
|
||||
return
|
||||
return LocMountMkdir, 0, err1
|
||||
}
|
||||
}
|
||||
// mount(source, target, fsType, flags, data)
|
||||
@ -417,7 +437,7 @@ func pivotAndMount(r *Runner, pivotRoot *byte) (err1 syscall.Errno) {
|
||||
uintptr(unsafe.Pointer(m.Target)), uintptr(unsafe.Pointer(m.FsType)), uintptr(m.Flags),
|
||||
uintptr(unsafe.Pointer(m.Data)), 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocMount, i, err1
|
||||
}
|
||||
// bind mount is not respect ro flag so that read-only bind mount needs remount
|
||||
if m.Flags&bindRo == bindRo {
|
||||
@ -425,7 +445,7 @@ func pivotAndMount(r *Runner, pivotRoot *byte) (err1 syscall.Errno) {
|
||||
uintptr(unsafe.Pointer(m.Target)), uintptr(unsafe.Pointer(m.FsType)),
|
||||
uintptr(m.Flags|syscall.MS_REMOUNT), uintptr(unsafe.Pointer(m.Data)), 0)
|
||||
if err1 != 0 {
|
||||
return
|
||||
return LocMount, i, err1
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -465,5 +485,5 @@ func pivotAndMount(r *Runner, pivotRoot *byte) (err1 syscall.Errno) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return 0
|
||||
return
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ func syncWithChild(r *Runner, p [2]int, pid int, err1 syscall.Errno) (int, error
|
||||
err2 syscall.Errno
|
||||
err error
|
||||
unshareUser = r.CloneFlags&unix.CLONE_NEWUSER == unix.CLONE_NEWUSER
|
||||
childErr ChildError
|
||||
)
|
||||
|
||||
// sync with child
|
||||
@ -73,7 +74,9 @@ func syncWithChild(r *Runner, p [2]int, pid int, err1 syscall.Errno) (int, error
|
||||
// clone syscall failed
|
||||
if err1 != 0 {
|
||||
unix.Close(p[0])
|
||||
return 0, syscall.Errno(err1)
|
||||
childErr.Location = LocClone
|
||||
childErr.Err = err1
|
||||
return 0, childErr
|
||||
}
|
||||
|
||||
// synchronize with child for uid / gid map
|
||||
@ -84,10 +87,10 @@ func syncWithChild(r *Runner, p [2]int, pid int, err1 syscall.Errno) (int, error
|
||||
syscall.RawSyscall(syscall.SYS_WRITE, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
}
|
||||
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&childErr)), uintptr(unsafe.Sizeof(childErr)))
|
||||
// child returned error code
|
||||
if r1 != unsafe.Sizeof(err2) || err2 != 0 || err1 != 0 {
|
||||
err = handlePipeError(r1, err2)
|
||||
if (r1 != unsafe.Sizeof(err2) && r1 != unsafe.Sizeof(childErr)) || childErr.Err != 0 || err1 != 0 {
|
||||
childErr.Err = handlePipeError(r1, childErr.Err)
|
||||
goto fail
|
||||
}
|
||||
|
||||
@ -104,17 +107,17 @@ func syncWithChild(r *Runner, p [2]int, pid int, err1 syscall.Errno) (int, error
|
||||
if r.Ptrace || r.StopBeforeSeccomp {
|
||||
// let's wait it in another goroutine to avoid SIGPIPE
|
||||
go func() {
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&childErr)), uintptr(unsafe.Sizeof(childErr)))
|
||||
unix.Close(p[0])
|
||||
}()
|
||||
return int(pid), nil
|
||||
}
|
||||
|
||||
// if read anything mean child failed after sync (close_on_exec so it should not block)
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&childErr)), uintptr(unsafe.Sizeof(childErr)))
|
||||
unix.Close(p[0])
|
||||
if r1 != 0 || err1 != 0 {
|
||||
err = handlePipeError(r1, err2)
|
||||
childErr.Err = handlePipeError(r1, childErr.Err)
|
||||
goto failAfterClose
|
||||
}
|
||||
return int(pid), nil
|
||||
@ -124,12 +127,15 @@ fail:
|
||||
|
||||
failAfterClose:
|
||||
handleChildFailed(int(pid))
|
||||
return 0, err
|
||||
if childErr.Err == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return 0, childErr
|
||||
}
|
||||
|
||||
// check pipe error
|
||||
func handlePipeError(r1 uintptr, errno syscall.Errno) error {
|
||||
if r1 == unsafe.Sizeof(errno) {
|
||||
func handlePipeError(r1 uintptr, errno syscall.Errno) syscall.Errno {
|
||||
if r1 >= unsafe.Sizeof(errno) {
|
||||
return syscall.Errno(errno)
|
||||
}
|
||||
return syscall.EPIPE
|
||||
|
Loading…
Reference in New Issue
Block a user