Implementing clone3

Updates #8585

PiperOrigin-RevId: 554554034
This commit is contained in:
Shambhavi Srivastava
2023-08-07 12:19:32 -07:00
committed by gVisor bot
parent 8ff6816f07
commit 21d66119b7
5 changed files with 128 additions and 6 deletions
+7
View File
@@ -46,9 +46,16 @@ const (
// Only passable via clone3(2).
CLONE_CLEAR_SIGHAND = 0x100000000
CLONE_INTO_CGROUP = 0x200000000
// Sizeof first published struct.
CLONE_ARGS_SIZE_VER0 = 64
// Sizeof third published struct.
CLONE_ARGS_SIZE_VER2 = 88
)
// CloneArgs is struct clone_args, from include/uapi/linux/sched.h.
//
// +marshal
type CloneArgs struct {
Flags uint64
Pidfd uint64
+17 -1
View File
@@ -30,6 +30,16 @@ import (
"gvisor.dev/gvisor/pkg/usermem"
)
// SupportedFlags is the bitwise OR of all the supported flags for clone.
// TODO(b/290826530): Implement CLONE_INTO_CGROUP when cgroups v2 is
// implemented.
const SupportedFlags = linux.CLONE_VM | linux.CLONE_FS | linux.CLONE_FILES | linux.CLONE_SYSVSEM |
linux.CLONE_THREAD | linux.CLONE_SIGHAND | linux.CLONE_CHILD_SETTID | linux.CLONE_NEWPID |
linux.CLONE_CHILD_CLEARTID | linux.CLONE_CHILD_SETTID | linux.CLONE_PARENT |
linux.CLONE_PARENT_SETTID | linux.CLONE_SETTLS | linux.CLONE_NEWUSER | linux.CLONE_NEWUTS |
linux.CLONE_NEWIPC | linux.CLONE_NEWNET | linux.CLONE_PTRACE | linux.CLONE_UNTRACED |
linux.CLONE_IO | linux.CLONE_VFORK
// Clone implements the clone(2) syscall and returns the thread ID of the new
// task in t's PID namespace. Clone may return both a non-zero thread ID and a
// non-nil error.
@@ -37,12 +47,18 @@ import (
// Preconditions: The caller must be running Task.doSyscallInvoke on the task
// goroutine.
func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
if args.Flags&^SupportedFlags != 0 {
return 0, nil, linuxerr.EINVAL
}
// Since signal actions may refer to application signal handlers by virtual
// address, any set of signal handlers must refer to the same address
// space.
if args.Flags&(linux.CLONE_SIGHAND|linux.CLONE_VM) == linux.CLONE_SIGHAND {
return 0, nil, linuxerr.EINVAL
}
if args.SetTID != 0 {
return 0, nil, linuxerr.ENOTSUP
}
// In order for the behavior of thread-group-directed signals to be sane,
// all tasks in a thread group must share signal handlers.
if args.Flags&(linux.CLONE_THREAD|linux.CLONE_SIGHAND) == linux.CLONE_THREAD {
@@ -160,7 +176,7 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
// clone() returns 0 in the child.
image.Arch.SetReturn(0)
if args.Stack != 0 {
image.Arch.SetStack(uintptr(args.Stack))
image.Arch.SetStack(uintptr(args.Stack + args.StackSize))
}
if args.Flags&linux.CLONE_SETTLS != 0 {
if !image.Arch.SetTLS(uintptr(args.TLS)) {
+4 -4
View File
@@ -108,7 +108,7 @@ var AMD64 = &kernel.SyscallTable{
53: syscalls.SupportedPoint("socketpair", SocketPair, PointSocketpair),
54: syscalls.Supported("setsockopt", SetSockOpt),
55: syscalls.Supported("getsockopt", GetSockOpt),
56: syscalls.PartiallySupportedPoint("clone", Clone, PointClone, "Mount namespace (CLONE_NEWNS) not supported. Options CLONE_PARENT, CLONE_SYSVSEM not supported.", nil),
56: syscalls.PartiallySupportedPoint("clone", Clone, PointClone, "Options CLONE_PIDFD, CLONE_NEWNS, CLONE_PARENT, CLONE_NEWTIME, CLONE_CLEAR_SIGHAND, and CLONE_SYSVSEM not supported.", nil),
57: syscalls.SupportedPoint("fork", Fork, PointFork),
58: syscalls.SupportedPoint("vfork", Vfork, PointVfork),
59: syscalls.SupportedPoint("execve", Execve, PointExecve),
@@ -403,7 +403,7 @@ var AMD64 = &kernel.SyscallTable{
432: syscalls.ErrorWithEvent("fsmount", linuxerr.ENOSYS, "", nil),
433: syscalls.ErrorWithEvent("fspick", linuxerr.ENOSYS, "", nil),
434: syscalls.ErrorWithEvent("pidfd_open", linuxerr.ENOSYS, "", nil),
435: syscalls.ErrorWithEvent("clone3", linuxerr.ENOSYS, "", nil),
435: syscalls.PartiallySupported("clone3", Clone3, "Options CLONE_PIDFD, CLONE_NEWNS, CLONE_INTO_CGROUP, CLONE_NEWTIME, CLONE_CLEAR_SIGHAND, CLONE_PARENT, CLONE_SYSVSEM and, SetTid are not supported.", nil),
436: syscalls.Supported("close_range", CloseRange),
439: syscalls.Supported("faccessat2", Faccessat2),
441: syscalls.Supported("epoll_pwait2", EpollPwait2),
@@ -651,7 +651,7 @@ var ARM64 = &kernel.SyscallTable{
217: syscalls.Error("add_key", linuxerr.EACCES, "Not available to user.", nil),
218: syscalls.Error("request_key", linuxerr.EACCES, "Not available to user.", nil),
219: syscalls.Error("keyctl", linuxerr.EACCES, "Not available to user.", nil),
220: syscalls.PartiallySupportedPoint("clone", Clone, PointClone, "Mount namespace (CLONE_NEWNS) not supported. Options CLONE_PARENT, CLONE_SYSVSEM not supported.", nil),
220: syscalls.PartiallySupportedPoint("clone", Clone, PointClone, "Options CLONE_PIDFD, CLONE_NEWNS, CLONE_PARENT, CLONE_NEWTIME, CLONE_CLEAR_SIGHAND, and CLONE_SYSVSEM not supported.", nil),
221: syscalls.SupportedPoint("execve", Execve, PointExecve),
222: syscalls.Supported("mmap", Mmap),
223: syscalls.PartiallySupported("fadvise64", Fadvise64, "Not all options are supported.", nil),
@@ -724,7 +724,7 @@ var ARM64 = &kernel.SyscallTable{
432: syscalls.ErrorWithEvent("fsmount", linuxerr.ENOSYS, "", nil),
433: syscalls.ErrorWithEvent("fspick", linuxerr.ENOSYS, "", nil),
434: syscalls.ErrorWithEvent("pidfd_open", linuxerr.ENOSYS, "", nil),
435: syscalls.ErrorWithEvent("clone3", linuxerr.ENOSYS, "", nil),
435: syscalls.PartiallySupported("clone3", Clone3, "Options CLONE_PIDFD, CLONE_NEWNS, CLONE_INTO_CGROUP, CLONE_NEWTIME, CLONE_CLEAR_SIGHAND, CLONE_PARENT, CLONE_SYSVSEM and clone_args.set_tid are not supported.", nil),
436: syscalls.Supported("close_range", CloseRange),
439: syscalls.Supported("faccessat2", Faccessat2),
441: syscalls.Supported("epoll_pwait2", EpollPwait2),
+23 -1
View File
@@ -206,7 +206,6 @@ func ExitGroup(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintp
func clone(t *kernel.Task, flags int, stack hostarch.Addr, parentTID hostarch.Addr, childTID hostarch.Addr, tls hostarch.Addr) (uintptr, *kernel.SyscallControl, error) {
args := linux.CloneArgs{
Flags: uint64(uint32(flags) &^ linux.CSIGNAL),
Pidfd: uint64(parentTID),
ChildTID: uint64(childTID),
ParentTID: uint64(parentTID),
ExitSignal: uint64(flags & linux.CSIGNAL),
@@ -234,6 +233,29 @@ func Vfork(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
return clone(t, linux.CLONE_VM|linux.CLONE_VFORK|int(linux.SIGCHLD), 0, 0, 0, 0)
}
// Clone3 implements linux syscall clone3(2).
func Clone3(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
cloneArgsPointer := args[0].Pointer()
size := args[1].SizeT()
if int(size) < linux.CLONE_ARGS_SIZE_VER0 || int(size) > linux.CLONE_ARGS_SIZE_VER2 {
return 0, nil, linuxerr.EINVAL
}
var cloneArgs linux.CloneArgs
if cloneArgsPointer != 0 {
if _, err := cloneArgs.CopyInN(t, cloneArgsPointer, int(size)); err != nil {
return 0, nil, err
}
}
ntid, ctrl, err := t.Clone(&cloneArgs)
if err != nil {
return 0, nil, err
}
return uintptr(ntid), ctrl, err
}
// parseCommonWaitOptions applies the options common to wait4 and waitid to
// wopts.
func parseCommonWaitOptions(wopts *kernel.WaitOptions, options int) error {
+77
View File
@@ -18,12 +18,16 @@
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
@@ -459,6 +463,79 @@ TEST(CloneTest, NonCanonicalTLS) {
#endif
}
#ifndef SYS_clone3
#define SYS_clone3 435
#endif // SYS_clone3
// struct clone_args is a Linux clone struct. Old versions of glibc do not
// expose it. See include/uapi/linux/sched.h
struct clone_args {
uint64_t flags;
uint64_t pidfd;
uint64_t child_tid;
uint64_t parent_tid;
uint64_t exit_signal;
uint64_t stack;
uint64_t stack_size;
uint64_t tls;
uint64_t set_tid;
uint64_t set_tid_size;
uint64_t cgroup;
};
int clone3(struct clone_args* ca, size_t size) {
return syscall(SYS_clone3, ca, size);
}
// Checks that clone fails for any unsupported flag.
TEST(CloneTest, Clone3UnknownFlag) {
clone_args ca = {};
ca.flags = (1ULL << 63);
ca.exit_signal = SIGCHLD;
EXPECT_THAT(clone3(&ca, sizeof(ca)), SyscallFailsWithErrno(EINVAL));
}
// Clone3 works as Clone.
TEST(CloneTest, Clone3AsClone) {
clone_args ca = {};
ca.exit_signal = SIGCHLD;
int child_pid;
EXPECT_THAT(child_pid = clone3(&ca, sizeof(ca)), SyscallSucceeds());
if (child_pid == 0) {
exit(0);
}
int status;
EXPECT_THAT(waitpid(child_pid, &status, 0),
SyscallSucceedsWithValue(child_pid));
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
// Clone3 works with basic Clone3 values for args like exit_signal & parent_tid.
TEST(CloneTest, Clone3Basic) {
clone_args ca = {};
ca.flags = CLONE_PARENT_SETTID;
ca.exit_signal = SIGCHLD;
pid_t store_child_tid = 0;
ca.parent_tid = reinterpret_cast<uint64_t>(&store_child_tid);
int child_pid;
EXPECT_THAT(child_pid = clone3(&ca, sizeof(ca)), SyscallSucceeds());
EXPECT_EQ(store_child_tid, child_pid);
if (child_pid == 0) {
exit(0);
}
int status;
EXPECT_THAT(waitpid(child_pid, &status, 0),
SyscallSucceedsWithValue(child_pid));
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
} // namespace
} // namespace testing
} // namespace gvisor