// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kernel

import (
	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/atomicbitops"
	"gvisor.dev/gvisor/pkg/cleanup"
	"gvisor.dev/gvisor/pkg/errors/linuxerr"
	"gvisor.dev/gvisor/pkg/hostarch"
	"gvisor.dev/gvisor/pkg/marshal/primitive"
	"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
	"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
	"gvisor.dev/gvisor/pkg/sentry/inet"
	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
	"gvisor.dev/gvisor/pkg/sentry/seccheck"
	pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/usermem"
)

// SupportedCloneFlags is the bitwise OR of all the supported flags for clone.
const SupportedCloneFlags = linux.CLONE_VM | linux.CLONE_FS | linux.CLONE_FILES | linux.CLONE_SYSVSEM |
	linux.CLONE_THREAD | linux.CLONE_SIGHAND | 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 | linux.CLONE_DETACHED | linux.CLONE_NEWNS |
	linux.CLONE_PIDFD | linux.CLONE_CLEAR_SIGHAND | linux.CLONE_INTO_CGROUP |
	linux.CLONE_NEWCGROUP

func failCloneAfterTaskCreation(nt *Task) {
	// nt has been visible to the rest of the system since NewTask, so
	// it may be blocking execve or a group stop, have been notified
	// for group signal delivery, had children reparented to it, etc.
	// Thus we can't just drop it on the floor. Instead, instruct the
	// task goroutine to exit immediately, as quietly as possible.
	nt.k.tasks.mu.Lock()
	nt.exitTracerNotified = true
	nt.exitTracerAcked = true
	nt.exitParentNotified = true
	nt.exitParentAcked = true
	nt.k.tasks.mu.Unlock()
	nt.runState = (*runExitMain)(nil)
}

// 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.
//
// 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&^SupportedCloneFlags != 0 {
		return 0, nil, linuxerr.EINVAL
	}
	if !t.k.Cgroup2FS().EverMounted() && args.Flags&(linux.CLONE_NEWCGROUP|linux.CLONE_INTO_CGROUP) != 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
	}

	// CLONE_SIGHAND and CLONE_CLEAR_SIGHAND are mutually exclusive.
	if args.Flags&(linux.CLONE_SIGHAND|linux.CLONE_CLEAR_SIGHAND) == linux.CLONE_SIGHAND|linux.CLONE_CLEAR_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 {
		return 0, nil, linuxerr.EINVAL
	}
	// All tasks in a thread group must be in the same PID namespace.
	if (args.Flags&linux.CLONE_THREAD != 0) && (args.Flags&linux.CLONE_NEWPID != 0 || t.childPIDNamespace != nil) {
		return 0, nil, linuxerr.EINVAL
	}
	// The two different ways of specifying a new PID namespace are
	// incompatible.
	if args.Flags&linux.CLONE_NEWPID != 0 && t.childPIDNamespace != nil {
		return 0, nil, linuxerr.EINVAL
	}
	// Thread groups and FS contexts cannot span user namespaces.
	if args.Flags&linux.CLONE_NEWUSER != 0 && args.Flags&(linux.CLONE_THREAD|linux.CLONE_FS) != 0 {
		return 0, nil, linuxerr.EINVAL
	}
	// args.ExitSignal must be a valid signal.
	if args.ExitSignal != 0 && !linux.Signal(args.ExitSignal).IsValid() {
		return 0, nil, linuxerr.EINVAL
	}
	if args.Flags&(linux.CLONE_FS|linux.CLONE_NEWNS) == linux.CLONE_FS|linux.CLONE_NEWNS {
		return 0, nil, linuxerr.EINVAL
	}

	// The CLONE_PARENT flag can't be used in clone calls by an init process.
	if args.Flags&linux.CLONE_PARENT != 0 && t.tg.IsInitIn(t.PIDNamespace()) {
		return 0, nil, linuxerr.EINVAL
	}

	// Pull task registers and FPU state, a cloned task will inherit the
	// state of the current task.
	if err := t.p.PullFullState(t.MemoryManager().AddressSpace(), t.Arch()); err != nil {
		t.Warningf("Unable to pull a full state: %v", err)
		t.forceSignal(linux.SIGILL, true /* unconditional */)
		t.SendSignal(SignalInfoPriv(linux.SIGILL))
		return 0, nil, linuxerr.EFAULT
	}

	// "If CLONE_NEWUSER is specified along with other CLONE_NEW* flags in a
	// single clone(2) or unshare(2) call, the user namespace is guaranteed to
	// be created first, giving the child (clone(2)) or caller (unshare(2))
	// privileges over the remaining namespaces created by the call." -
	// user_namespaces(7)
	creds := t.Credentials()
	userns := creds.UserNamespace
	cu := cleanup.Make(func() {})
	defer cu.Clean()
	if args.Flags&linux.CLONE_NEWUSER != 0 {
		var err error
		// "EPERM (since Linux 3.9): CLONE_NEWUSER was specified in flags and
		// the caller is in a chroot environment (i.e., the caller's root
		// directory does not match the root directory of the mount namespace
		// in which it resides)." - clone(2). Neither chroot(2) nor
		// user_namespaces(7) document this.
		if t.IsChrooted() {
			return 0, nil, linuxerr.EPERM
		}
		userns, err = creds.NewChildUserNamespace()
		if err != nil {
			return 0, nil, err
		}
		userns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, userns))
	} else {
		userns.IncRef()
	}
	cu.Add(func() {
		userns.DecRef(t)
	})
	if args.Flags&(linux.CLONE_NEWPID|linux.CLONE_NEWNET|linux.CLONE_NEWUTS|linux.CLONE_NEWIPC|linux.CLONE_NEWCGROUP) != 0 && !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, userns) {
		return 0, nil, linuxerr.EPERM
	}

	utsns := t.utsns
	if args.Flags&linux.CLONE_NEWUTS != 0 {
		// Note that this must happen after NewUserNamespace so we get
		// the new userns if there is one.
		utsns = utsns.Clone(userns)
		utsns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, utsns))
	} else {
		utsns.IncRef()
	}
	cu.Add(func() {
		utsns.DecRef(t)
	})

	ipcns := t.ipcns
	if args.Flags&linux.CLONE_NEWIPC != 0 {
		ipcns = NewIPCNamespace(userns)
		ipcns.InitPosixQueues(t, t.k.VFS(), creds)
		ipcns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, ipcns))
	} else {
		ipcns.IncRef()
	}
	cu.Add(func() {
		ipcns.DecRef(t)
	})

	cgroupns := t.cgroupns
	if args.Flags&linux.CLONE_NEWCGROUP != 0 {
		// The new namespace is rooted at the calling task's current cgroup,
		// even if the child is placed elsewhere via CLONE_INTO_CGROUP. See
		// Linux's copy_cgroup_ns().
		cgroupns = newCgroupNamespace(t.Cgroup2(), userns)
		cgroupns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, cgroupns))
	} else {
		cgroupns.IncRef()
	}
	cu.Add(func() {
		cgroupns.DecRef(t)
	})

	netns := t.netns
	if args.Flags&linux.CLONE_NEWNET != 0 {
		netns = inet.NewNamespace(netns, userns)
		inode := nsfs.NewInode(t, t.k.nsfsMount, netns)
		netns.SetInode(inode)
	} else {
		netns.IncRef()
	}
	cu.Add(func() {
		netns.DecRef(t)
	})

	// We must hold t.mu to access t.image, but we can't hold it during Fork(),
	// since TaskImage.Fork()=>mm.Fork() takes mm.addressSpaceMu, which is ordered
	// above Task.mu. So we copy t.image with t.mu held and call Fork() on the copy.
	t.mu.Lock()
	curImage := t.image
	sessionKeyring := t.sessionKeyring
	t.mu.Unlock()
	image, err := curImage.Fork(t, t.k, args.Flags&linux.CLONE_VM != 0)
	if err != nil {
		return 0, nil, err
	}
	cu.Add(func() {
		image.release(t)
	})

	if args.Flags&linux.CLONE_NEWUSER != 0 {
		// If the task is in a new user namespace, it cannot share keys.
		sessionKeyring = nil
	}

	// clone() returns 0 in the child.
	image.Arch.SetReturn(0)
	if args.Stack != 0 {
		image.Arch.SetStack(uintptr(args.Stack + args.StackSize))
	}
	if args.Flags&linux.CLONE_SETTLS != 0 {
		if !image.Arch.SetTLS(uintptr(args.TLS)) {
			return 0, nil, linuxerr.EPERM
		}
	}

	var fsContext *FSContext
	if args.Flags&linux.CLONE_FS == 0 || args.Flags&linux.CLONE_NEWNS != 0 {
		fsContext = t.FSContext().Fork()
	} else {
		fsContext = t.FSContext()
		if !fsContext.share() {
			// Linux fails clone with EAGAIN if there is a concurrent execve, see kernel/fork.c:copy_fs().
			return 0, nil, linuxerr.EAGAIN
		}
	}

	mntns := t.mountNamespace
	if args.Flags&linux.CLONE_NEWNS != 0 {
		var err error
		mntns, err = t.k.vfs.CloneMountNamespace(t, userns, mntns, &fsContext.root, &fsContext.cwd, t.k)
		if err != nil {
			return 0, nil, err
		}
	} else {
		mntns.IncRef()
	}
	cu.Add(func() {
		mntns.DecRef(t)
	})

	var fdTable *FDTable
	if args.Flags&linux.CLONE_FILES == 0 {
		fdTable = t.fdTable.Fork(t, MaxFdLimit)
	} else {
		fdTable = t.fdTable
		fdTable.IncRef()
	}

	pidns := t.tg.pidns
	if t.childPIDNamespace != nil {
		pidns = t.childPIDNamespace
	} else if args.Flags&linux.CLONE_NEWPID != 0 {
		pidns = pidns.NewChild(t, t.k, userns)
		defer pidns.DecRef(t)
	}

	tg := t.tg
	rseqAddr := hostarch.Addr(0)
	rseqSignature := uint32(0)
	if args.Flags&linux.CLONE_THREAD == 0 {
		sh := t.tg.signalHandlers
		if args.Flags&linux.CLONE_SIGHAND == 0 {
			if args.Flags&linux.CLONE_CLEAR_SIGHAND == 0 {
				// copy the existing signal handlers
				sh = sh.Fork()
			} else {
				// CLONE_CLEAR_SIGHAND: reset non-SIG_IGN handlers to SIG_DFL
				sh = sh.Reset()
			}
		}
		termSig := linux.Signal(args.ExitSignal)
		if args.Flags&linux.CLONE_PARENT != 0 {
			t.tg.pidns.owner.mu.RLock()
			termSig = t.tg.terminationSignal
			t.tg.pidns.owner.mu.RUnlock()
		}
		tg = t.k.NewThreadGroup(pidns, sh, termSig, tg.limits.GetCopy())
		tg.oomScoreAdj = atomicbitops.FromInt32(t.tg.oomScoreAdj.Load())
		tg.coredumpFilter = atomicbitops.FromUint32(t.tg.coredumpFilter.Load())
		rseqAddr = t.rseqAddr
		rseqSignature = t.rseqSignature
	}

	uc := t.userCounters
	if uc.uid != creds.RealKUID {
		uc = t.k.GetUserCounters(creds.RealKUID)
	}
	childCreds := creds
	if userns != creds.UserNamespace {
		childCreds = creds.ForkIntoUserNamespace(userns)
	}

	cfg := &TaskConfig{
		Kernel:           t.k,
		ThreadGroup:      tg,
		SignalMask:       t.SignalMask(),
		TaskImage:        image,
		FSContext:        fsContext,
		FDTable:          fdTable,
		Credentials:      childCreds,
		NoNewPrivs:       t.GetNoNewPrivs(),
		Niceness:         t.Niceness(),
		NetworkNamespace: netns,
		AllowedCPUMask:   t.CPUMask(),
		UTSNamespace:     utsns,
		IPCNamespace:     ipcns,
		CgroupNamespace:  cgroupns,
		MountNamespace:   mntns,
		RSeqAddr:         rseqAddr,
		RSeqSignature:    rseqSignature,
		ContainerID:      t.ContainerID(),
		UserCounters:     uc,
		SessionKeyring:   sessionKeyring,
		Personality:      t.personality.Load(),
		Origin:           t.Origin,
		cgroupFD:         args.Cgroup,
		cloneIntoCgroup:  args.Flags&linux.CLONE_INTO_CGROUP != 0,
	}
	if args.Flags&(linux.CLONE_THREAD|linux.CLONE_PARENT) == 0 {
		cfg.Parent = t
	} else {
		cfg.InheritParent = t
	}
	if args.Flags&linux.CLONE_PIDFD != 0 {
		cfg.makePIDFD = true
	}
	nt, err := t.tg.pidns.owner.NewTask(t, cfg)
	// If NewTask succeeds, we transfer references to nt. If cloneNewTask fails, it does
	// the cleanup for us.
	cu.Release()
	if err != nil {
		if args.Flags&linux.CLONE_THREAD == 0 {
			tg.Release(t)
		}
		return 0, nil, err
	}

	// "A child process created via fork(2) inherits a copy of its parent's
	// alternate signal stack settings" - sigaltstack(2).
	//
	// However kernel/fork.c:copy_process() adds a limitation to this:
	// "sigaltstack should be cleared when sharing the same VM".
	if args.Flags&linux.CLONE_VM == 0 || args.Flags&linux.CLONE_VFORK != 0 {
		nt.SetSignalStack(t.SignalStack())
	}

	// This has to happen last, because e.g. ptraceClone may send a SIGSTOP to
	// nt that it must receive before its task goroutine starts running.
	tid := nt.k.tasks.Root.IDOfTask(nt)
	defer nt.Start(tid)

	var pidFD int32 = -1
	if args.Flags&linux.CLONE_PIDFD != 0 {
		isThread := args.Flags&linux.CLONE_THREAD != 0
		vfsfd, err := t.pidFDOpen(nt.pid, isThread, false /*nonblock*/)
		if err != nil {
			failCloneAfterTaskCreation(nt)
			return 0, nil, err
		}
		defer vfsfd.DecRef(t)

		fd, err := t.NewFDFrom(0, vfsfd, FDFlags{CloseOnExec: true})
		if err != nil {
			failCloneAfterTaskCreation(nt)
			return 0, nil, err
		}
		pidFD = fd
		if _, err := primitive.CopyInt32Out(t, hostarch.Addr(args.Pidfd), fd); err != nil {
			failCloneAfterTaskCreation(nt)
			if oldFile := t.FDTable().Remove(t, fd); oldFile != nil {
				oldFile.DecRef(t)
			}
			return 0, nil, err
		}
	}

	if seccheck.Global.Enabled(seccheck.PointClone) {
		mask, info := getCloneSeccheckInfo(t, nt, args.Flags)
		if err := seccheck.Global.SentToSinks(func(c seccheck.Sink) error {
			return c.Clone(t, mask, info)
		}); err != nil {
			failCloneAfterTaskCreation(nt)
			if pidFD != -1 {
				if oldFile := t.FDTable().Remove(t, pidFD); oldFile != nil {
					oldFile.DecRef(t)
				}
			}
			return 0, nil, err
		}
	}

	// "If fork/clone and execve are allowed by @prog, any child processes will
	// be constrained to the same filters and system call ABI as the parent." -
	// Documentation/prctl/seccomp_filter.txt
	if ts := t.seccomp.Load(); ts != nil {
		seccompCopy := ts.copy()
		seccompCopy.populateCache(nt)
		nt.seccomp.Store(seccompCopy)
	} else {
		nt.seccomp.Store(nil)
	}
	if args.Flags&linux.CLONE_VFORK != 0 {
		nt.vforkParent.Store(t)
	}

	if args.Flags&linux.CLONE_CHILD_CLEARTID != 0 {
		nt.SetClearTID(hostarch.Addr(args.ChildTID))
	}
	if args.Flags&linux.CLONE_CHILD_SETTID != 0 {
		ctid := nt.ThreadID()
		ctid.CopyOut(nt.CopyContext(t, usermem.IOOpts{}), hostarch.Addr(args.ChildTID))
	}
	ntid := t.tg.pidns.IDOfTask(nt)
	if args.Flags&linux.CLONE_PARENT_SETTID != 0 {
		ntid.CopyOut(t, hostarch.Addr(args.ParentTID))
	}

	t.traceCloneEvent(tid)
	kind := ptraceCloneKindClone
	if args.Flags&linux.CLONE_VFORK != 0 {
		kind = ptraceCloneKindVfork
	} else if linux.Signal(args.ExitSignal) == linux.SIGCHLD {
		kind = ptraceCloneKindFork
	}
	if t.ptraceClone(kind, nt, args) {
		if args.Flags&linux.CLONE_VFORK != 0 {
			return ntid, &SyscallControl{next: &runSyscallAfterPtraceEventClone{vforkChild: nt, vforkChildTID: ntid}}, nil
		}
		return ntid, &SyscallControl{next: &runSyscallAfterPtraceEventClone{}}, nil
	}
	if args.Flags&linux.CLONE_VFORK != 0 {
		t.maybeBeginVforkStop(nt)
		return ntid, &SyscallControl{next: &runSyscallAfterVforkStop{childTID: ntid}}, nil
	}
	return ntid, nil, nil
}

func getCloneSeccheckInfo(t, nt *Task, flags uint64) (seccheck.FieldSet, *pb.CloneInfo) {
	fields := seccheck.Global.GetFieldSet(seccheck.PointClone)
	var cwd string
	if fields.Context.Contains(seccheck.FieldCtxtCwd) {
		cwd = getTaskCurrentWorkingDirectory(t)
	}
	t.k.tasks.mu.RLock()
	defer t.k.tasks.mu.RUnlock()
	info := &pb.CloneInfo{
		CreatedThreadId:          int32(nt.k.tasks.Root.tids[nt]),
		CreatedThreadGroupId:     int32(nt.k.tasks.Root.tgids[nt.tg]),
		CreatedThreadStartTimeNs: nt.startTime.Nanoseconds(),
		Flags:                    flags,
	}

	if !fields.Context.Empty() {
		info.ContextData = &pb.ContextData{}
		LoadSeccheckDataLocked(t, fields.Context, info.ContextData, cwd)
	}

	return fields, info
}

// maybeBeginVforkStop checks if a previously-started vfork child is still
// running and has not yet released its MM, such that its parent t should enter
// a vforkStop.
//
// Preconditions: The caller must be running on t's task goroutine.
func (t *Task) maybeBeginVforkStop(child *Task) {
	if child.vforkParent.Load() != t {
		return
	}
	t.tg.pidns.owner.mu.Lock()
	defer t.tg.pidns.owner.mu.Unlock()
	t.tg.signalHandlers.mu.Lock()
	defer t.tg.signalHandlers.mu.Unlock()
	if child.vforkParent.Load() != t {
		return
	}
	if t.killedLocked() {
		child.vforkParent.Store(nil)
		return
	}
	t.beginInternalStopLocked((*vforkStop)(nil))
}

func (t *Task) unstopVforkParent() {
	if t.vforkParent.Load() == nil {
		return
	}
	t.tg.pidns.owner.mu.Lock()
	defer t.tg.pidns.owner.mu.Unlock()
	if p := t.vforkParent.Load(); p != nil {
		t.vforkParent.Store(nil)
		p.tg.signalHandlers.mu.Lock()
		defer p.tg.signalHandlers.mu.Unlock()
		if _, ok := p.stop.(*vforkStop); ok {
			p.endInternalStopLocked()
		}
	}
}

// +stateify savable
type runSyscallAfterPtraceEventClone struct {
	vforkChild *Task

	// If vforkChild is not nil, vforkChildTID is its thread ID in the parent's
	// PID namespace. vforkChildTID must be stored since the child may exit and
	// release its TID before the PTRACE_EVENT stop ends.
	vforkChildTID ThreadID
}

func (r *runSyscallAfterPtraceEventClone) execute(t *Task) taskRunState {
	if r.vforkChild != nil {
		t.maybeBeginVforkStop(r.vforkChild)
		return &runSyscallAfterVforkStop{r.vforkChildTID}
	}
	return (*runSyscallExit)(nil)
}

// +stateify savable
type runSyscallAfterVforkStop struct {
	// childTID has the same meaning as
	// runSyscallAfterPtraceEventClone.vforkChildTID.
	childTID ThreadID
}

func (r *runSyscallAfterVforkStop) execute(t *Task) taskRunState {
	t.ptraceVforkDone(r.childTID)
	return (*runSyscallExit)(nil)
}

type namespaceSet struct {
	childPIDNS *PIDNamespace
	netNS      *inet.Namespace
	utsNS      *UTSNamespace
	ipcNS      *IPCNamespace
	cgroupNS   *CgroupNamespace
	mountNS    *vfs.MountNamespace
	userNS     *auth.UserNamespace

	fsContext *FSContext
}

func (nss *namespaceSet) release(t *Task) {
	if nss.childPIDNS != nil {
		nss.childPIDNS.DecRef(t)
	}
	if nss.netNS != nil {
		nss.netNS.DecRef(t)
	}
	if nss.utsNS != nil {
		nss.utsNS.DecRef(t)
	}
	if nss.ipcNS != nil {
		nss.ipcNS.DecRef(t)
	}
	if nss.cgroupNS != nil {
		nss.cgroupNS.DecRef(t)
	}
	if nss.mountNS != nil {
		nss.mountNS.DecRef(t)
	}
	if nss.userNS != nil {
		nss.userNS.DecRef(t)
	}

	if nss.fsContext != nil {
		nss.fsContext.DecRef(t)
	}
}

func (nss *namespaceSet) initFromTask(t *Task, target *Task, flags int32) error {
	supported := uint32(linux.CLONE_NEWPID | linux.CLONE_NEWNET | linux.CLONE_NEWUTS | linux.CLONE_NEWIPC | linux.CLONE_NEWNS | linux.CLONE_NEWUSER)
	if target.k.Cgroup2FS().EverMounted() {
		supported |= linux.CLONE_NEWCGROUP
	}
	if (uint32(flags) & ^supported) != 0 || flags == 0 {
		return linuxerr.EINVAL
	}
	if !t.CanTrace(target, false) {
		return linuxerr.EPERM
	}

	if flags&linux.CLONE_NEWPID != 0 {
		nss.childPIDNS = target.tg.pidns
		if nss.childPIDNS == nil {
			return linuxerr.ESRCH
		}
		// We need to lock the TaskSet mutex to avoid racing with a dying target tg: if we lose
		// that race, we might end up resuscitating a dead pidns (we'd actually end up panicking
		// the refcounting code). Namespaces other than the pidns kind are cleared for an exiting task.
		// A more fundamental difference is that all but the pidns reside in the struct Task, whereas
		// a pidns resides in the struct ThreadGroup and is immutable and never cleared.
		target.tg.pidns.owner.mu.RLock()
		if target.ExitState() >= TaskExitInitiated {
			nss.childPIDNS = nil
			target.tg.pidns.owner.mu.RUnlock()
			return linuxerr.ESRCH
		}
		nss.childPIDNS.IncRef()
		target.tg.pidns.owner.mu.RUnlock()
	}

	target.mu.Lock()
	defer target.mu.Unlock()

	if flags&linux.CLONE_NEWUSER != 0 {
		// User namespaces are stored in credentials, which outlive the other
		// namespace fields cleared during task exit.
		if target.ExitState() >= TaskExitInitiated {
			return linuxerr.ESRCH
		}
		nss.userNS = target.Credentials().UserNamespace
		nss.userNS.IncRef()
	}
	if flags&linux.CLONE_NEWNET != 0 {
		nss.netNS = target.netns
		if nss.netNS == nil {
			return linuxerr.ESRCH
		}
		nss.netNS.IncRef()
	}
	if flags&linux.CLONE_NEWUTS != 0 {
		nss.utsNS = target.utsns
		if nss.utsNS == nil {
			return linuxerr.ESRCH
		}
		nss.utsNS.IncRef()
	}
	if flags&linux.CLONE_NEWIPC != 0 {
		nss.ipcNS = target.ipcns
		if nss.ipcNS == nil {
			return linuxerr.ESRCH
		}
		nss.ipcNS.IncRef()
	}
	if flags&linux.CLONE_NEWCGROUP != 0 {
		nss.cgroupNS = target.cgroupns
		if nss.cgroupNS == nil {
			return linuxerr.ESRCH
		}
		nss.cgroupNS.IncRef()
	}
	if flags&linux.CLONE_NEWNS != 0 {
		nss.mountNS = target.mountNamespace
		if nss.mountNS == nil {
			return linuxerr.ESRCH
		}
		nss.mountNS.IncRef()
	}

	return nil
}

func (nss *namespaceSet) initFromNS(ns vfs.Namespace, flags int32) error {
	switch ns := ns.(type) {
	case *PIDNamespace:
		if flags != 0 && flags != linux.CLONE_NEWPID {
			return linuxerr.EINVAL
		}
		nss.childPIDNS = ns
		ns.IncRef()
	case *inet.Namespace:
		if flags != 0 && flags != linux.CLONE_NEWNET {
			return linuxerr.EINVAL
		}
		nss.netNS = ns
		ns.IncRef()
	case *UTSNamespace:
		if flags != 0 && flags != linux.CLONE_NEWUTS {
			return linuxerr.EINVAL
		}
		nss.utsNS = ns
		ns.IncRef()
	case *IPCNamespace:
		if flags != 0 && flags != linux.CLONE_NEWIPC {
			return linuxerr.EINVAL
		}
		nss.ipcNS = ns
		ns.IncRef()
	case *CgroupNamespace:
		if flags != 0 && flags != linux.CLONE_NEWCGROUP {
			return linuxerr.EINVAL
		}
		nss.cgroupNS = ns
		ns.IncRef()
	case *vfs.MountNamespace:
		if flags != 0 && flags != linux.CLONE_NEWNS {
			return linuxerr.EINVAL
		}
		nss.mountNS = ns
		ns.IncRef()
	case *auth.UserNamespace:
		if flags != 0 && flags != linux.CLONE_NEWUSER {
			return linuxerr.EINVAL
		}
		nss.userNS = ns
		ns.IncRef()
	default:
		return linuxerr.EINVAL
	}

	return nil
}

// Setns reassociates task t with the specified namespace(s).
func (t *Task) Setns(fd *vfs.FileDescription, flags int32) error {
	if !t.k.Cgroup2FS().EverMounted() && flags&linux.CLONE_NEWCGROUP != 0 {
		return linuxerr.EINVAL
	}
	var nss namespaceSet
	defer nss.release(t)

	target, err := PIDFDTask(fd)
	switch err {
	case nil:
		// fd is a pidfd pointing to a healthy task.
		if err := nss.initFromTask(t, target, flags); err != nil {
			return err
		}
	case linuxerr.EBADF:
		// fd is not a pidfd, try treating it as an nsfd.
		d, ok := fd.Dentry().Impl().(*kernfs.Dentry)
		if !ok {
			return linuxerr.EINVAL // Linux returns EINVAL in this case.
		}
		i, ok := d.Inode().(*nsfs.Inode)
		if !ok {
			return linuxerr.EINVAL // Linux returns EINVAL in this case.
		}
		if _, ok := i.Namespace().(*CgroupNamespace); ok && !t.k.Cgroup2FS().EverMounted() {
			return linuxerr.EINVAL
		}
		if err := nss.initFromNS(i.Namespace(), flags); err != nil {
			return err
		}
	default:
		return err
	}

	creds := t.Credentials()
	checkCreds := creds
	if nss.userNS != nil {
		if nss.userNS == creds.UserNamespace {
			return linuxerr.EINVAL
		}
		t.tg.signalHandlers.mu.Lock()
		if t.tg.tasksCount != 1 {
			t.tg.signalHandlers.mu.Unlock()
			return linuxerr.EINVAL
		}
		t.tg.signalHandlers.mu.Unlock()
		if t.FSContext().ReadRefs() != 1 {
			return linuxerr.EINVAL
		}
		if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, nss.userNS) {
			return linuxerr.EPERM
		}
		checkCreds = creds.ForkIntoUserNamespace(nss.userNS)
	}

	if nss.childPIDNS != nil {
		if !checkCreds.HasCapabilityIn(linux.CAP_SYS_ADMIN, nss.childPIDNS.UserNamespace()) || !checkCreds.HasSelfCapability(linux.CAP_SYS_ADMIN) {
			return linuxerr.EPERM
		}
		// Allow setting the current or a child pid namespace.
		current := t.PIDNamespace()
		ancestor := nss.childPIDNS
		for ; ancestor != nil; ancestor = ancestor.parent {
			if ancestor == current {
				break
			}
		}
		if ancestor == nil {
			return linuxerr.EINVAL
		}
	}

	if nss.netNS != nil {
		if !checkCreds.HasCapabilityIn(linux.CAP_SYS_ADMIN, nss.netNS.UserNamespace()) || !checkCreds.HasSelfCapability(linux.CAP_SYS_ADMIN) {
			return linuxerr.EPERM
		}
	}

	if nss.utsNS != nil {
		if !checkCreds.HasCapabilityIn(linux.CAP_SYS_ADMIN, nss.utsNS.UserNamespace()) || !checkCreds.HasSelfCapability(linux.CAP_SYS_ADMIN) {
			return linuxerr.EPERM
		}
	}

	if nss.ipcNS != nil {
		if !checkCreds.HasCapabilityIn(linux.CAP_SYS_ADMIN, nss.ipcNS.UserNamespace()) || !checkCreds.HasSelfCapability(linux.CAP_SYS_ADMIN) {
			return linuxerr.EPERM
		}
	}

	if nss.cgroupNS != nil {
		if !checkCreds.HasCapabilityIn(linux.CAP_SYS_ADMIN, nss.cgroupNS.UserNamespace()) || !checkCreds.HasSelfCapability(linux.CAP_SYS_ADMIN) {
			return linuxerr.EPERM
		}
	}

	if nss.mountNS != nil {
		if !checkCreds.HasCapabilityIn(linux.CAP_SYS_ADMIN, nss.mountNS.UserNamespace()) || !checkCreds.HasSelfCapability(linux.CAP_SYS_CHROOT) || !checkCreds.HasSelfCapability(linux.CAP_SYS_ADMIN) {
			return linuxerr.EPERM
		}
		oldFSContext := t.FSContext()
		// The current task has to be an exclusive owner of its fs context.
		if oldFSContext.ReadRefs() != 1 {
			return linuxerr.EINVAL
		}
		nss.fsContext = oldFSContext.Fork()
		nss.fsContext.root.DecRef(t)
		nss.fsContext.cwd.DecRef(t)
		vd := nss.mountNS.Root(t)
		nss.fsContext.root = vd
		vd.IncRef()
		nss.fsContext.cwd = vd
	}

	// Swap to new namespaces.
	// Store replaced resources in nss so that they're cleaned up by the deferred function.
	t.mu.Lock()
	if nss.userNS != nil {
		t.creds.Store(checkCreds)
		nss.userNS = creds.UserNamespace
	}
	if nss.childPIDNS != nil {
		t.childPIDNamespace, nss.childPIDNS = nss.childPIDNS, t.childPIDNamespace
	}
	if nss.netNS != nil {
		t.netns, nss.netNS = nss.netNS, t.netns
	}
	if nss.utsNS != nil {
		t.utsns, nss.utsNS = nss.utsNS, t.utsns
	}
	if nss.ipcNS != nil {
		t.ipcns, nss.ipcNS = nss.ipcNS, t.ipcns
	}
	if nss.cgroupNS != nil {
		t.cgroupns, nss.cgroupNS = nss.cgroupNS, t.cgroupns
	}
	if nss.mountNS != nil {
		t.mountNamespace, nss.mountNS = nss.mountNS, t.mountNamespace
		tmp := t.FSContext()
		t.fsContext.Store(nss.fsContext)
		nss.fsContext = tmp
	}
	t.mu.Unlock()
	return nil
}

// Unshare changes the set of resources t shares with other tasks, as specified
// by flags.
//
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) Unshare(flags int32) error {
	// "CLONE_THREAD, CLONE_SIGHAND, and CLONE_VM can be specified in flags if
	// the caller is single threaded (i.e., it is not sharing its address space
	// with another process or thread). In this case, these flags have no
	// effect. (Note also that specifying CLONE_THREAD automatically implies
	// CLONE_VM, and specifying CLONE_VM automatically implies CLONE_SIGHAND.)
	// If the process is multithreaded, then the use of these flags results in
	// an error." - unshare(2). This is incorrect (cf.
	// kernel/fork.c:ksys_unshare()):
	//
	//	- CLONE_THREAD does not imply CLONE_VM.
	//
	//	- CLONE_SIGHAND implies CLONE_THREAD.
	//
	//	- Only CLONE_VM requires that the caller is not sharing its address
	//		space with another thread. CLONE_SIGHAND requires that the caller is not
	//		sharing its signal handlers, and CLONE_THREAD requires that the caller
	//		is the only thread in its thread group.
	//
	// Since we don't count the number of tasks using each address space or set
	// of signal handlers, we reject CLONE_VM and CLONE_SIGHAND altogether.
	if flags&(linux.CLONE_VM|linux.CLONE_SIGHAND) != 0 {
		return linuxerr.EINVAL
	}
	if !t.k.Cgroup2FS().EverMounted() && flags&linux.CLONE_NEWCGROUP != 0 {
		return linuxerr.EINVAL
	}
	if flags&linux.CLONE_THREAD != 0 {
		t.tg.signalHandlers.mu.Lock()
		if t.tg.tasksCount != 1 {
			t.tg.signalHandlers.mu.Unlock()
			return linuxerr.EINVAL
		}
		t.tg.signalHandlers.mu.Unlock()
		// This isn't racy because we're the only living task, and therefore
		// the only task capable of creating new ones, in our thread group.
	}

	// Prepare new execution context.
	creds := t.Credentials()
	originalUserNS := creds.UserNamespace
	var (
		newFSContext          *FSContext
		oldFSContextToDestroy *FSContext
		newFDTable            *FDTable
		newCreds              bool
		newUserNS             *auth.UserNamespace
		newChildPIDNS         *PIDNamespace
		newNetNS              *inet.Namespace
		newUTSNS              *UTSNamespace
		newIPCNS              *IPCNamespace
		newCgroupNS           *CgroupNamespace
		newMountNS            *vfs.MountNamespace
	)
	defer func() {
		if newFSContext != nil {
			newFSContext.DecRef(t)
		}
		if oldFSContextToDestroy != nil {
			oldFSContextToDestroy.destroy(t)
		}
		if newFDTable != nil {
			newFDTable.DecRef(t)
		}
		if newUserNS != nil {
			newUserNS.DecRef(t)
		}
		if newNetNS != nil {
			newNetNS.DecRef(t)
		}
		if newUTSNS != nil {
			newUTSNS.DecRef(t)
		}
		if newIPCNS != nil {
			newIPCNS.DecRef(t)
		}
		if newCgroupNS != nil {
			newCgroupNS.DecRef(t)
		}
		if newMountNS != nil {
			newMountNS.DecRef(t)
		}
	}()
	if flags&linux.CLONE_FS != 0 || flags&linux.CLONE_NEWNS != 0 {
		newFSContext = t.FSContext().Fork()
	}
	if flags&linux.CLONE_FILES != 0 {
		newFDTable = t.fdTable.Fork(t, MaxFdLimit)
	}
	if flags&linux.CLONE_NEWUSER != 0 {
		if t.IsChrooted() {
			return linuxerr.EPERM
		}
		var err error
		newUserNS, err = creds.NewChildUserNamespace()
		if err != nil {
			return err
		}
		newUserNS.SetInode(nsfs.NewInode(t, t.k.nsfsMount, newUserNS))
		creds = t.Credentials().ForkIntoUserNamespace(newUserNS)
		newCreds = true
	}
	if flags&(linux.CLONE_NEWPID|linux.CLONE_NEWNET|linux.CLONE_NEWUTS|linux.CLONE_NEWIPC|linux.CLONE_NEWCGROUP|linux.CLONE_NEWNS) != 0 {
		if !creds.HasSelfCapability(linux.CAP_SYS_ADMIN) {
			return linuxerr.EPERM
		}
	}
	if flags&linux.CLONE_NEWPID != 0 {
		newChildPIDNS = t.tg.pidns.NewChild(t, t.k, creds.UserNamespace)
	}
	if flags&linux.CLONE_NEWNET != 0 {
		newNetNS = inet.NewNamespace(t.netns, creds.UserNamespace)
		newNetNS.SetInode(nsfs.NewInode(t, t.k.nsfsMount, newNetNS))
	}
	if flags&linux.CLONE_NEWUTS != 0 {
		newUTSNS = t.utsns.Clone(creds.UserNamespace)
		newUTSNS.SetInode(nsfs.NewInode(t, t.k.nsfsMount, newUTSNS))
	}
	if flags&linux.CLONE_NEWIPC != 0 {
		newIPCNS = NewIPCNamespace(creds.UserNamespace)
		newIPCNS.InitPosixQueues(t, t.k.VFS(), creds)
		newIPCNS.SetInode(nsfs.NewInode(t, t.k.nsfsMount, newIPCNS))
	}
	if flags&linux.CLONE_NEWCGROUP != 0 {
		newCgroupNS = newCgroupNamespace(t.Cgroup2(), creds.UserNamespace)
		newCgroupNS.SetInode(nsfs.NewInode(t, t.k.nsfsMount, newCgroupNS))
	}
	if flags&linux.CLONE_NEWNS != 0 {
		fsContext := newFSContext
		if fsContext == nil {
			fsContext = t.FSContext()
		}
		var err error
		newMountNS, err = t.k.vfs.CloneMountNamespace(t, creds.UserNamespace, t.mountNamespace, &fsContext.root, &fsContext.cwd, t.k)
		if err != nil {
			return err
		}
	}

	// Switch to new execution context. Store replaced resources in new* so
	// that they're cleaned up by the deferred function.
	t.mu.Lock()
	defer t.mu.Unlock()
	if newCreds {
		t.creds.Store(creds)
		newUserNS = originalUserNS
	}
	if newFSContext != nil {
		oldFSContext := t.FSContext()
		// unshareFromTask() lowers the old fs context's ref count, but it's
		// for us to destroy it if there are no other references.
		if oldFSContext.unshareFromTask(t, newFSContext) {
			oldFSContextToDestroy = oldFSContext
		}
		// newFSContext is now installed into the task.
		// Clear it so the deferred cleanup doesn't decref it.
		newFSContext = nil
	}
	if newFDTable != nil {
		t.fdTable, newFDTable = newFDTable, t.fdTable
	}
	if newChildPIDNS != nil {
		t.childPIDNamespace = newChildPIDNS
	}
	if newNetNS != nil {
		t.netns, newNetNS = newNetNS, t.netns
	}
	if newUTSNS != nil {
		t.utsns, newUTSNS = newUTSNS, t.utsns
	}
	if newIPCNS != nil {
		t.ipcns, newIPCNS = newIPCNS, t.ipcns
	}
	if newCgroupNS != nil {
		t.cgroupns, newCgroupNS = newCgroupNS, t.cgroupns
	}
	if newMountNS != nil {
		t.mountNamespace, newMountNS = newMountNS, t.mountNamespace
	}

	return nil
}

// UnshareFdTable unshares the FdTable that task t shares with other tasks, upto
// the maxFd.
//
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) UnshareFdTable(maxFd int32) {
	t.mu.Lock()
	oldFDTable := t.fdTable
	t.fdTable = oldFDTable.Fork(t, maxFd)
	t.mu.Unlock()

	oldFDTable.DecRef(t)
}

// vforkStop is a TaskStop imposed on a task that creates a child with
// CLONE_VFORK or vfork(2), that ends when the child task ceases to use its
// current MM. (Normally, CLONE_VFORK is used in conjunction with CLONE_VM, so
// that the child and parent share mappings until the child execve()s into a
// new process image or exits.)
//
// +stateify savable
type vforkStop struct{}

// StopIgnoresKill implements TaskStop.Killable.
func (*vforkStop) Killable() bool { return true }
