forkexec: initial support for clone3

This commit is contained in:
criyle 2025-02-20 03:56:49 -05:00
parent f6f057edf9
commit 3d8333e952
4 changed files with 45 additions and 1 deletions

View File

@ -185,6 +185,8 @@ type Environment interface {
- 6.1: `pids.peak` in cgroup v2 - 6.1: `pids.peak` in cgroup v2
- 5.19: `memory.peak` in cgroup v2 - 5.19: `memory.peak` in cgroup v2
- 5.7: `clone3` with `CLONE_INTO_CGROUP`
- 5.3: `clone3`
- 4.15: cgroup v2 (also need support in the Linux distribution) - 4.15: cgroup v2 (also need support in the Linux distribution)
- 4.14: SECCOMP_RET_KILL_PROCESS - 4.14: SECCOMP_RET_KILL_PROCESS
- 4.6: CLONE_NEWCGROUP - 4.6: CLONE_NEWCGROUP

View File

@ -0,0 +1,17 @@
package forkexec
// cloneArgs holds arguments for clone3 Linux syscall.
// from src/syscall/exec_linux.go:196
type cloneArgs struct {
flags uint64 // Flags bit mask
pidFD uint64 // Where to store PID file descriptor (int *)
childTID uint64 // Where to store child TID, in child's memory (pid_t *)
parentTID uint64 // Where to store child TID, in parent's memory (pid_t *)
exitSignal uint64 // Signal to deliver to parent on child termination
stack uint64 // Pointer to lowest byte of stack
stackSize uint64 // Size of stack
tls uint64 // Location of new TLS
setTID uint64 // Pointer to a pid_t array (since Linux 5.5)
setTIDSize uint64 // Number of elements in set_tid (since Linux 5.5)
cgroup uint64 // File descriptor for target cgroup of child (since Linux 5.7)
}

View File

@ -1,6 +1,7 @@
package forkexec package forkexec
import ( import (
"runtime"
"syscall" "syscall"
"unsafe" "unsafe"
@ -11,9 +12,21 @@ import (
// //
//go:norace //go:norace
func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, hostname, domainname, pivotRoot *byte, p [2]int) (r1 uintptr, err1 syscall.Errno) { func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, hostname, domainname, pivotRoot *byte, p [2]int) (r1 uintptr, err1 syscall.Errno) {
var (
clone3 *cloneArgs
)
// similar to exec_linux, avoid side effect by shuffling around // similar to exec_linux, avoid side effect by shuffling around
fd, nextfd := prepareFds(r.Files) fd, nextfd := prepareFds(r.Files)
// use clone3 if cgroupFd specified
if r.CgroupFd > 0 {
clone3 = &cloneArgs{
flags: uint64(r.CloneFlags) | unix.CLONE_INTO_CGROUP,
exitSignal: uint64(syscall.SIGCHLD),
cgroup: uint64(r.CgroupFd),
}
}
// Acquire the fork lock so that no other threads // Acquire the fork lock so that no other threads
// create new fds that are not yet close-on-exec // create new fds that are not yet close-on-exec
// before we fork. // before we fork.
@ -24,7 +37,16 @@ func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, host
beforeFork() beforeFork()
// UnshareFlags (new namespaces) is activated by clone syscall // UnshareFlags (new namespaces) is activated by clone syscall
r1, _, err1 = syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|(r.CloneFlags&UnshareFlags), 0, 0, 0, 0, 0) if clone3 != nil {
r1, _, err1 = syscall.RawSyscall6(unix.SYS_CLONE3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3), 0, 0, 0, 0)
} else {
if runtime.GOARCH == "s390x" {
// On Linux/s390, the first two arguments of clone(2) are swapped.
r1, _, err1 = syscall.RawSyscall6(syscall.SYS_CLONE, 0, uintptr(syscall.SIGCHLD)|(r.CloneFlags&UnshareFlags), 0, 0, 0, 0)
} else {
r1, _, err1 = syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|(r.CloneFlags&UnshareFlags), 0, 0, 0, 0, 0)
}
}
if err1 != 0 || r1 != 0 { if err1 != 0 || r1 != 0 {
// in parent process, immediate return // in parent process, immediate return
return return

View File

@ -61,6 +61,9 @@ type Runner struct {
UIDMappings []syscall.SysProcIDMap UIDMappings []syscall.SysProcIDMap
GIDMappings []syscall.SysProcIDMap GIDMappings []syscall.SysProcIDMap
// CgroupFd to use when clone3 with CLONE_INTO_CGROUP with kernel >=5.7 and cgroup v2
CgroupFd uintptr
// Credential holds user and group identities to be assumed // Credential holds user and group identities to be assumed
// by a child process started by StartProcess. // by a child process started by StartProcess.
Credential *syscall.Credential Credential *syscall.Credential