// 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 }