mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement the setns syscall
This change introduces the nsfs file system. Each new namespace allocates a new nsfs inode. Here are reasons why we need these inodes: * each namespace has to have an unique id. * proc/pid/ns/ contains one entry for each namespace. Bind mounting one of the files in this directory to somewhere else in the filesystem keeps the corresponding namespace alive even if all processes currently in the namespace terminate. * setns() allows the calling process to join an existing namespace specified by a file descriptor. PiperOrigin-RevId: 550694515
This commit is contained in:
@@ -24,6 +24,7 @@ const (
|
||||
EXT_SUPER_MAGIC = 0xef53
|
||||
FUSE_SUPER_MAGIC = 0x65735546
|
||||
MQUEUE_MAGIC = 0x19800202
|
||||
NSFS_MAGIC = 0x6e736673
|
||||
OVERLAYFS_SUPER_MAGIC = 0x794c7630
|
||||
PIPEFS_MAGIC = 0x50495045
|
||||
PROC_SUPER_MAGIC = 0x9fa0
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
|
||||
package(default_applicable_licenses = ["//:license"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_template_instance(
|
||||
name = "inode_refs",
|
||||
out = "inode_refs.go",
|
||||
package = "nsfs",
|
||||
prefix = "inode",
|
||||
template = "//pkg/refs:refs_template",
|
||||
types = {
|
||||
"T": "Inode",
|
||||
},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "nsfs",
|
||||
srcs = [
|
||||
"inode_refs.go",
|
||||
"nsfs.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/atomicbitops",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/refs",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,205 @@
|
||||
// Copyright 2023 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 nsfs provides the filesystem implementation backing
|
||||
// Kernel.NsfsMount.
|
||||
package nsfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
// +stateify savable
|
||||
type filesystemType struct{}
|
||||
|
||||
// Name implements vfs.FilesystemType.Name.
|
||||
func (filesystemType) Name() string {
|
||||
return "nsfs"
|
||||
}
|
||||
|
||||
// Release implements vfs.FilesystemType.Release.
|
||||
func (filesystemType) Release(ctx context.Context) {}
|
||||
|
||||
// GetFilesystem implements vfs.FilesystemType.GetFilesystem.
|
||||
func (filesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
|
||||
panic("nsfs.filesystemType.GetFilesystem should never be called")
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type filesystem struct {
|
||||
kernfs.Filesystem
|
||||
|
||||
devMinor uint32
|
||||
}
|
||||
|
||||
// NewFilesystem sets up and returns a new vfs.Filesystem implemented by nsfs.
|
||||
func NewFilesystem(vfsObj *vfs.VirtualFilesystem) (*vfs.Filesystem, error) {
|
||||
devMinor, err := vfsObj.GetAnonBlockDevMinor()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs := &filesystem{
|
||||
devMinor: devMinor,
|
||||
}
|
||||
fs.Filesystem.VFSFilesystem().Init(vfsObj, filesystemType{}, fs)
|
||||
return fs.Filesystem.VFSFilesystem(), nil
|
||||
}
|
||||
|
||||
// Release implements vfs.FilesystemImpl.Release.
|
||||
func (fs *filesystem) Release(ctx context.Context) {
|
||||
fs.Filesystem.VFSFilesystem().VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
|
||||
fs.Filesystem.Release(ctx)
|
||||
}
|
||||
|
||||
// MountOptions implements vfs.FilesystemImpl.MountOptions.
|
||||
func (fs *filesystem) MountOptions() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Inode implements kernfs.Inode.
|
||||
//
|
||||
// +stateify savable
|
||||
type Inode struct {
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNotAnonymous
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
inodeRefs
|
||||
|
||||
locks vfs.FileLocks
|
||||
namespace Namespace
|
||||
|
||||
mnt *vfs.Mount
|
||||
}
|
||||
|
||||
// DecRef implements kernfs.Inode.DecRef.
|
||||
func (i *Inode) DecRef(ctx context.Context) {
|
||||
i.inodeRefs.DecRef(func() { i.namespace.Destroy(ctx) })
|
||||
}
|
||||
|
||||
// Keep implements kernfs.Inode.Keep.
|
||||
func (i *Inode) Keep() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Namespace is the namespace interface.
|
||||
type Namespace interface {
|
||||
Type() string
|
||||
Destroy(ctx context.Context)
|
||||
}
|
||||
|
||||
// NewInode creates a new nsfs inode.
|
||||
func NewInode(ctx context.Context, mnt *vfs.Mount, namespace Namespace) *Inode {
|
||||
fs := mnt.Filesystem().Impl().(*filesystem)
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
i := &Inode{
|
||||
namespace: namespace,
|
||||
mnt: mnt,
|
||||
}
|
||||
i.InodeAttrs.Init(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.Filesystem.NextIno(), nsfsMode)
|
||||
i.InitRefs()
|
||||
return i
|
||||
}
|
||||
|
||||
const nsfsMode = linux.S_IFREG | linux.ModeUserRead | linux.ModeGroupRead | linux.ModeOtherRead
|
||||
|
||||
// Namespace returns the namespace associated with the inode.
|
||||
func (i *Inode) Namespace() Namespace {
|
||||
return i.namespace
|
||||
}
|
||||
|
||||
// Name returns the inode name that is used to implement readlink() of
|
||||
// /proc/pid/ns/ files.
|
||||
func (i *Inode) Name() string {
|
||||
return fmt.Sprintf("%s:[%d]", i.namespace.Type(), i.Ino())
|
||||
}
|
||||
|
||||
// VirtualDentry returns VirtualDentry for the inode.
|
||||
func (i *Inode) VirtualDentry() vfs.VirtualDentry {
|
||||
dentry := &kernfs.Dentry{}
|
||||
mnt := i.mnt
|
||||
fs := mnt.Filesystem().Impl().(*filesystem)
|
||||
i.IncRef()
|
||||
mnt.IncRef()
|
||||
dentry.Init(&fs.Filesystem, i)
|
||||
vd := vfs.MakeVirtualDentry(mnt, dentry.VFSDentry())
|
||||
return vd
|
||||
}
|
||||
|
||||
// Mode implements kernfs.Inode.Mode.
|
||||
func (i *Inode) Mode() linux.FileMode {
|
||||
return nsfsMode
|
||||
}
|
||||
|
||||
// SetStat implements kernfs.Inode.SetStat.
|
||||
//
|
||||
// Linux sets S_IMMUTABLE to nsfs inodes that prevents any attribute changes on
|
||||
// them.
|
||||
func (i *Inode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
|
||||
return linuxerr.EPERM
|
||||
}
|
||||
|
||||
// namespace FD is a synthetic file that represents a namespace in
|
||||
// /proc/[pid]/ns/*.
|
||||
//
|
||||
// +stateify savable
|
||||
type namespaceFD struct {
|
||||
vfs.FileDescriptionDefaultImpl
|
||||
vfs.LockFD
|
||||
|
||||
vfsfd vfs.FileDescription
|
||||
inode *Inode
|
||||
}
|
||||
|
||||
// Stat implements vfs.FileDescriptionImpl.Stat.
|
||||
func (fd *namespaceFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
vfs := fd.vfsfd.VirtualDentry().Mount().Filesystem()
|
||||
return fd.inode.Stat(ctx, vfs, opts)
|
||||
}
|
||||
|
||||
// SetStat implements vfs.FileDescriptionImpl.SetStat.
|
||||
func (fd *namespaceFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
vfs := fd.vfsfd.VirtualDentry().Mount().Filesystem()
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
return fd.inode.SetStat(ctx, vfs, creds, opts)
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *namespaceFD) Release(ctx context.Context) {
|
||||
fd.inode.DecRef(ctx)
|
||||
}
|
||||
|
||||
// Open implements kernfs.Inode.Open.
|
||||
func (i *Inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
fd := &namespaceFD{inode: i}
|
||||
i.IncRef()
|
||||
fd.LockFD.Init(&i.locks)
|
||||
if err := fd.vfsfd.Init(fd, opts.Flags, rp.Mount(), d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fd.vfsfd, nil
|
||||
}
|
||||
|
||||
// StatFS implements kernfs.Inode.StatFS.
|
||||
func (i *Inode) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, error) {
|
||||
return vfs.GenericStatFS(linux.NSFS_MAGIC), nil
|
||||
}
|
||||
@@ -91,6 +91,7 @@ go_library(
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/fsimpl/lock",
|
||||
"//pkg/sentry/fsimpl/nsfs",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -72,7 +72,7 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
|
||||
"mounts": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, &mountsData{fs: fs, task: task}),
|
||||
"net": fs.newTaskNetDir(ctx, task),
|
||||
"ns": fs.newTaskOwnedDir(ctx, task, fs.NextIno(), 0511, map[string]kernfs.Inode{
|
||||
"net": fs.newFakeNamespaceSymlink(ctx, task, fs.NextIno(), "net"),
|
||||
"net": fs.newNamespaceSymlink(ctx, task, fs.NextIno(), linux.CLONE_NEWNET),
|
||||
"pid": fs.newPIDNamespaceSymlink(ctx, task, fs.NextIno()),
|
||||
"user": fs.newFakeNamespaceSymlink(ctx, task, fs.NextIno(), "user"),
|
||||
}),
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/limits"
|
||||
@@ -1230,7 +1231,18 @@ func (i *mountsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
type namespaceSymlink struct {
|
||||
kernfs.StaticSymlink
|
||||
|
||||
task *kernel.Task
|
||||
task *kernel.Task
|
||||
nsType int
|
||||
}
|
||||
|
||||
func (fs *filesystem) newNamespaceSymlink(ctx context.Context, task *kernel.Task, ino uint64, nsType int) kernfs.Inode {
|
||||
inode := &namespaceSymlink{task: task, nsType: nsType}
|
||||
|
||||
// Note: credentials are overridden by taskOwnedInode.
|
||||
inode.Init(ctx, task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, ino, "")
|
||||
|
||||
taskInode := &taskOwnedInode{Inode: inode, owner: task}
|
||||
return taskInode
|
||||
}
|
||||
|
||||
func (fs *filesystem) newPIDNamespaceSymlink(ctx context.Context, task *kernel.Task, ino uint64) kernfs.Inode {
|
||||
@@ -1258,11 +1270,29 @@ func (fs *filesystem) newFakeNamespaceSymlink(ctx context.Context, task *kernel.
|
||||
return taskInode
|
||||
}
|
||||
|
||||
func (s *namespaceSymlink) getInode(t *kernel.Task) *nsfs.Inode {
|
||||
switch s.nsType {
|
||||
case linux.CLONE_NEWNET:
|
||||
return t.GetNetworkNamespace().GetInode()
|
||||
default:
|
||||
panic("unknown namespace")
|
||||
}
|
||||
}
|
||||
|
||||
// Readlink implements kernfs.Inode.Readlink.
|
||||
func (s *namespaceSymlink) Readlink(ctx context.Context, mnt *vfs.Mount) (string, error) {
|
||||
if err := checkTaskState(s.task); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if s.nsType != 0 {
|
||||
inode := s.getInode(s.task)
|
||||
if inode == nil {
|
||||
return "", linuxerr.ENOENT
|
||||
}
|
||||
target := inode.Name()
|
||||
inode.DecRef(ctx)
|
||||
return target, nil
|
||||
}
|
||||
return s.StaticSymlink.Readlink(ctx, mnt)
|
||||
}
|
||||
|
||||
@@ -1272,6 +1302,14 @@ func (s *namespaceSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.Vir
|
||||
return vfs.VirtualDentry{}, "", err
|
||||
}
|
||||
|
||||
if s.nsType != 0 {
|
||||
inode := s.getInode(s.task)
|
||||
if inode == nil {
|
||||
return vfs.VirtualDentry{}, "", linuxerr.ENOENT
|
||||
}
|
||||
defer inode.DecRef(ctx)
|
||||
return inode.VirtualDentry(), "", nil
|
||||
}
|
||||
// Create a synthetic inode to represent the namespace.
|
||||
fs := mnt.Filesystem().Impl().(*filesystem)
|
||||
nsInode := &namespaceInode{}
|
||||
|
||||
@@ -100,7 +100,6 @@ go_library(
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/pgalloc",
|
||||
"//pkg/sentry/socket/unix/transport",
|
||||
"//pkg/sentry/usage",
|
||||
"//pkg/state",
|
||||
"//pkg/sync",
|
||||
|
||||
@@ -44,6 +44,8 @@ go_library(
|
||||
"//pkg/atomicbitops",
|
||||
"//pkg/context",
|
||||
"//pkg/refs",
|
||||
"//pkg/sentry/fsimpl/nsfs",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/stack",
|
||||
],
|
||||
|
||||
@@ -14,11 +14,17 @@
|
||||
|
||||
package inet
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
)
|
||||
|
||||
// Namespace represents a network namespace. See network_namespaces(7).
|
||||
//
|
||||
// +stateify savable
|
||||
type Namespace struct {
|
||||
namespaceRefs
|
||||
inode *nsfs.Inode
|
||||
|
||||
// stack is the network stack implementation of this network namespace.
|
||||
stack Stack `state:"nosave"`
|
||||
@@ -32,38 +38,68 @@ type Namespace struct {
|
||||
|
||||
// isRoot indicates whether this is the root network namespace.
|
||||
isRoot bool
|
||||
|
||||
userNS *auth.UserNamespace
|
||||
}
|
||||
|
||||
// NewRootNamespace creates the root network namespace, with creator
|
||||
// allowing new network namespaces to be created. If creator is nil, no
|
||||
// networking will function if the network is namespaced.
|
||||
func NewRootNamespace(stack Stack, creator NetworkStackCreator) *Namespace {
|
||||
func NewRootNamespace(stack Stack, creator NetworkStackCreator, userNS *auth.UserNamespace) *Namespace {
|
||||
n := &Namespace{
|
||||
stack: stack,
|
||||
creator: creator,
|
||||
isRoot: true,
|
||||
userNS: userNS,
|
||||
}
|
||||
n.InitRefs()
|
||||
return n
|
||||
}
|
||||
|
||||
// UserNamespace returns the user namespace associated with this namespace.
|
||||
func (n *Namespace) UserNamespace() *auth.UserNamespace {
|
||||
return n.userNS
|
||||
}
|
||||
|
||||
// SetInode sets the nsfs `inode` to the namespace.
|
||||
func (n *Namespace) SetInode(inode *nsfs.Inode) {
|
||||
n.inode = inode
|
||||
}
|
||||
|
||||
// GetInode returns the nsfs inode associated with this namespace.
|
||||
func (n *Namespace) GetInode() *nsfs.Inode {
|
||||
return n.inode
|
||||
}
|
||||
|
||||
// NewNamespace creates a new network namespace from the root.
|
||||
func NewNamespace(root *Namespace) *Namespace {
|
||||
func NewNamespace(root *Namespace, userNS *auth.UserNamespace) *Namespace {
|
||||
n := &Namespace{
|
||||
creator: root.creator,
|
||||
userNS: userNS,
|
||||
}
|
||||
n.init()
|
||||
n.InitRefs()
|
||||
return n
|
||||
}
|
||||
|
||||
// Destroy implements nsfs.Namespace.Destroy.
|
||||
func (n *Namespace) Destroy(ctx context.Context) {
|
||||
if s := n.Stack(); s != nil {
|
||||
s.Destroy()
|
||||
}
|
||||
}
|
||||
|
||||
// Type implements nsfs.Namespace.Type.
|
||||
func (n *Namespace) Type() string {
|
||||
return "net"
|
||||
}
|
||||
|
||||
// IncRef increments the Namespace's refcount.
|
||||
func (n *Namespace) IncRef() {
|
||||
n.inode.IncRef()
|
||||
}
|
||||
|
||||
// DecRef decrements the Namespace's refcount.
|
||||
func (n *Namespace) DecRef() {
|
||||
n.namespaceRefs.DecRef(func() {
|
||||
if s := n.Stack(); s != nil {
|
||||
s.Destroy()
|
||||
}
|
||||
})
|
||||
func (n *Namespace) DecRef(ctx context.Context) {
|
||||
n.inode.DecRef(ctx)
|
||||
}
|
||||
|
||||
// Stack returns the network stack of n. Stack may return nil if no network
|
||||
|
||||
@@ -354,6 +354,7 @@ go_library(
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/fsimpl/lock",
|
||||
"//pkg/sentry/fsimpl/mqfs",
|
||||
"//pkg/sentry/fsimpl/nsfs",
|
||||
"//pkg/sentry/fsimpl/pipefs",
|
||||
"//pkg/sentry/fsimpl/sockfs",
|
||||
"//pkg/sentry/fsimpl/timerfd",
|
||||
|
||||
@@ -47,6 +47,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/timerfd"
|
||||
@@ -274,6 +275,9 @@ type Kernel struct {
|
||||
// syscalls (as opposed to named pipes created by mknod()).
|
||||
pipeMount *vfs.Mount
|
||||
|
||||
// nsfsMount is the Mount used for namespaces.
|
||||
nsfsMount *vfs.Mount
|
||||
|
||||
// shmMount is the Mount used for anonymous files created by the
|
||||
// memfd_create() syscalls. It is analogous to Linux's shm_mnt.
|
||||
shmMount *vfs.Mount
|
||||
@@ -395,7 +399,7 @@ func (k *Kernel) Init(args InitKernelArgs) error {
|
||||
k.rootAbstractSocketNamespace = args.RootAbstractSocketNamespace
|
||||
k.rootNetworkNamespace = args.RootNetworkNamespace
|
||||
if k.rootNetworkNamespace == nil {
|
||||
k.rootNetworkNamespace = inet.NewRootNamespace(nil, nil)
|
||||
k.rootNetworkNamespace = inet.NewRootNamespace(nil, nil, args.RootUserNamespace)
|
||||
}
|
||||
k.runningTasksCond.L = &k.runningTasksMu
|
||||
k.cpuClockTickerWakeCh = make(chan struct{}, 1)
|
||||
@@ -439,6 +443,15 @@ func (k *Kernel) Init(args InitKernelArgs) error {
|
||||
pipeMount := k.vfs.NewDisconnectedMount(pipeFilesystem, nil, &vfs.MountOptions{})
|
||||
k.pipeMount = pipeMount
|
||||
|
||||
nsfsFilesystem, err := nsfs.NewFilesystem(&k.vfs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create nsfs filesystem: %v", err)
|
||||
}
|
||||
defer nsfsFilesystem.DecRef(ctx)
|
||||
nsfsMount := k.vfs.NewDisconnectedMount(nsfsFilesystem, nil, &vfs.MountOptions{})
|
||||
k.nsfsMount = nsfsMount
|
||||
k.rootNetworkNamespace.SetInode(nsfs.NewInode(ctx, nsfsMount, k.rootNetworkNamespace))
|
||||
|
||||
tmpfsOpts := vfs.GetFilesystemOptions{
|
||||
InternalData: tmpfs.FilesystemOpts{
|
||||
// See mm/shmem.c:shmem_init() => vfs_kern_mount(flags=SB_KERNMOUNT).
|
||||
@@ -1607,6 +1620,11 @@ func (k *Kernel) PipeMount() *vfs.Mount {
|
||||
return k.pipeMount
|
||||
}
|
||||
|
||||
// NsfsMount returns the nsfs mount.
|
||||
func (k *Kernel) NsfsMount() *vfs.Mount {
|
||||
return k.nsfsMount
|
||||
}
|
||||
|
||||
// ShmMount returns the tmpfs mount.
|
||||
func (k *Kernel) ShmMount() *vfs.Mount {
|
||||
return k.shmMount
|
||||
@@ -1630,12 +1648,13 @@ func (k *Kernel) Release() {
|
||||
ctx := k.SupervisorContext()
|
||||
k.hostMount.DecRef(ctx)
|
||||
k.pipeMount.DecRef(ctx)
|
||||
k.nsfsMount.DecRef(ctx)
|
||||
k.shmMount.DecRef(ctx)
|
||||
k.socketMount.DecRef(ctx)
|
||||
k.vfs.Release(ctx)
|
||||
k.timekeeper.Destroy()
|
||||
k.vdso.Release(ctx)
|
||||
k.RootNetworkNamespace().DecRef()
|
||||
k.RootNetworkNamespace().DecRef(ctx)
|
||||
}
|
||||
|
||||
// PopulateNewCgroupHierarchy moves all tasks into a newly created cgroup
|
||||
|
||||
@@ -506,7 +506,9 @@ type Task struct {
|
||||
numaPolicy linux.NumaPolicy
|
||||
numaNodeMask uint64
|
||||
|
||||
// netns is the task's network namespace. netns is never nil.
|
||||
// netns is the task's network namespace. It has to be changed under mu
|
||||
// so that GetNetworkNamespace can take a reference before it is
|
||||
// released.
|
||||
netns inet.NamespaceAtomicPtr
|
||||
|
||||
// If rseqPreempted is true, before the next call to p.Switch(),
|
||||
|
||||
@@ -21,9 +21,12 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/cleanup"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"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/seccheck"
|
||||
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
@@ -117,14 +120,16 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
|
||||
})
|
||||
defer cu.Clean()
|
||||
|
||||
netns := t.NetworkNamespace()
|
||||
netns := t.netns.Load()
|
||||
if args.Flags&linux.CLONE_NEWNET != 0 {
|
||||
netns = inet.NewNamespace(netns)
|
||||
netns = inet.NewNamespace(netns, userns)
|
||||
inode := nsfs.NewInode(t, t.k.nsfsMount, netns)
|
||||
netns.SetInode(inode)
|
||||
} else {
|
||||
netns.IncRef()
|
||||
}
|
||||
cu.Add(func() {
|
||||
netns.DecRef()
|
||||
netns.DecRef(t)
|
||||
})
|
||||
|
||||
// TODO(b/63601033): Implement CLONE_NEWNS.
|
||||
@@ -405,6 +410,38 @@ func (r *runSyscallAfterVforkStop) execute(t *Task) taskRunState {
|
||||
return (*runSyscallExit)(nil)
|
||||
}
|
||||
|
||||
// Setns reassociates thread with the specified namespace.
|
||||
func (t *Task) Setns(fd *vfs.FileDescription, flags int32) error {
|
||||
d, ok := fd.Dentry().Impl().(*kernfs.Dentry)
|
||||
if !ok {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
i, ok := d.Inode().(*nsfs.Inode)
|
||||
if !ok {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
|
||||
switch ns := i.Namespace().(type) {
|
||||
case *inet.Namespace:
|
||||
if flags != 0 && flags != linux.CLONE_NEWNET {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, ns.UserNamespace()) ||
|
||||
!t.Credentials().HasCapability(linux.CAP_SYS_ADMIN) {
|
||||
return linuxerr.EPERM
|
||||
}
|
||||
oldNS := t.NetworkNamespace()
|
||||
ns.IncRef()
|
||||
t.mu.Lock()
|
||||
t.netns.Store(ns)
|
||||
t.mu.Unlock()
|
||||
oldNS.DecRef(t)
|
||||
return nil
|
||||
default:
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
// Unshare changes the set of resources t shares with other tasks, as specified
|
||||
// by flags.
|
||||
//
|
||||
@@ -456,7 +493,7 @@ func (t *Task) Unshare(flags int32) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Need to reload creds, becaue t.SetUserNamespace() changed task credentials.
|
||||
// Need to reload creds, because t.SetUserNamespace() changed task credentials.
|
||||
creds = t.Credentials()
|
||||
}
|
||||
haveCapSysAdmin := t.HasCapability(linux.CAP_SYS_ADMIN)
|
||||
@@ -466,13 +503,18 @@ func (t *Task) Unshare(flags int32) error {
|
||||
}
|
||||
t.childPIDNamespace = t.tg.pidns.NewChild(t.UserNamespace())
|
||||
}
|
||||
var oldNETNS *inet.Namespace
|
||||
if flags&linux.CLONE_NEWNET != 0 {
|
||||
if !haveCapSysAdmin {
|
||||
return linuxerr.EPERM
|
||||
}
|
||||
oldNETNS = t.netns.Load()
|
||||
t.netns.Store(inet.NewNamespace(t.netns.Load()))
|
||||
netns := t.NetworkNamespace()
|
||||
netns = inet.NewNamespace(netns, t.UserNamespace())
|
||||
netnsInode := nsfs.NewInode(t, t.k.nsfsMount, netns)
|
||||
netns.SetInode(netnsInode)
|
||||
t.mu.Lock()
|
||||
netns = t.netns.Swap(netns)
|
||||
t.mu.Unlock()
|
||||
netns.DecRef(t)
|
||||
}
|
||||
t.mu.Lock()
|
||||
// Can't defer unlock: DecRefs must occur without holding t.mu.
|
||||
@@ -511,9 +553,6 @@ func (t *Task) Unshare(flags int32) error {
|
||||
if oldIPCNS != nil {
|
||||
oldIPCNS.DecRef(t)
|
||||
}
|
||||
if oldNETNS != nil {
|
||||
oldNETNS.DecRef()
|
||||
}
|
||||
if oldFDTable != nil {
|
||||
oldFDTable.DecRef(t)
|
||||
}
|
||||
|
||||
@@ -288,13 +288,13 @@ func (*runExitMain) execute(t *Task) taskRunState {
|
||||
mntns := t.mountNamespace
|
||||
t.mountNamespace = nil
|
||||
ipcns := t.ipcns
|
||||
netns := t.NetworkNamespace()
|
||||
netns := t.netns.Swap(nil)
|
||||
t.mu.Unlock()
|
||||
if mntns != nil {
|
||||
mntns.DecRef(t)
|
||||
}
|
||||
ipcns.DecRef(t)
|
||||
netns.DecRef()
|
||||
netns.DecRef(t)
|
||||
|
||||
// If this is the last task to exit from the thread group, release the
|
||||
// thread group's resources.
|
||||
|
||||
@@ -36,3 +36,17 @@ func (t *Task) NetworkContext() inet.Stack {
|
||||
func (t *Task) NetworkNamespace() *inet.Namespace {
|
||||
return t.netns.Load()
|
||||
}
|
||||
|
||||
// GetNetworkNamespace takes a reference on the task network namespace and
|
||||
// returns it. It can return nil if the task isn't alive.
|
||||
func (t *Task) GetNetworkNamespace() *inet.Namespace {
|
||||
// t.mu is required to be sure that the network namespace will not be
|
||||
// released.
|
||||
t.mu.Lock()
|
||||
netns := t.netns.Load()
|
||||
if netns != nil {
|
||||
netns.IncRef()
|
||||
}
|
||||
t.mu.Unlock()
|
||||
return netns
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func (ts *TaskSet) NewTask(ctx context.Context, cfg *TaskConfig) (*Task, error)
|
||||
cfg.FSContext.DecRef(ctx)
|
||||
cfg.FDTable.DecRef(ctx)
|
||||
cfg.IPCNamespace.DecRef(ctx)
|
||||
cfg.NetworkNamespace.DecRef()
|
||||
cfg.NetworkNamespace.DecRef(ctx)
|
||||
if cfg.MountNamespace != nil {
|
||||
cfg.MountNamespace.DecRef(ctx)
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ func (s *sock) Release(ctx context.Context) {
|
||||
_ = t.BlockWithDeadline(ch, true, deadline)
|
||||
}
|
||||
}
|
||||
s.namespace.DecRef()
|
||||
s.namespace.DecRef(ctx)
|
||||
}
|
||||
|
||||
// Epollable implements FileDescriptionImpl.Epollable.
|
||||
|
||||
@@ -97,7 +97,6 @@ go_library(
|
||||
"//pkg/log",
|
||||
"//pkg/refs",
|
||||
"//pkg/sentry/hostfd",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/uniqueid",
|
||||
"//pkg/sync",
|
||||
"//pkg/sync/locking",
|
||||
|
||||
@@ -360,7 +360,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
305: syscalls.CapError("clock_adjtime", linux.CAP_SYS_TIME, "", nil),
|
||||
306: syscalls.Supported("syncfs", Syncfs),
|
||||
307: syscalls.Supported("sendmmsg", SendMMsg),
|
||||
308: syscalls.ErrorWithEvent("setns", linuxerr.EOPNOTSUPP, "Needs filesystem support", []string{"gvisor.dev/issue/140"}), // TODO(b/29354995)
|
||||
308: syscalls.Supported("setns", Setns),
|
||||
309: syscalls.Supported("getcpu", Getcpu),
|
||||
310: syscalls.ErrorWithEvent("process_vm_readv", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/158"}), // TODO(b/260724654)
|
||||
311: syscalls.ErrorWithEvent("process_vm_writev", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/158"}), // TODO(b/260724654)
|
||||
@@ -683,7 +683,7 @@ var ARM64 = &kernel.SyscallTable{
|
||||
265: syscalls.Error("open_by_handle_at", linuxerr.EOPNOTSUPP, "Not supported by gVisor filesystems", nil),
|
||||
266: syscalls.CapError("clock_adjtime", linux.CAP_SYS_TIME, "", nil),
|
||||
267: syscalls.Supported("syncfs", Syncfs),
|
||||
268: syscalls.ErrorWithEvent("setns", linuxerr.EOPNOTSUPP, "Needs filesystem support", []string{"gvisor.dev/issue/140"}), // TODO(b/29354995)
|
||||
268: syscalls.Supported("setns", Setns),
|
||||
269: syscalls.Supported("sendmmsg", SendMMsg),
|
||||
270: syscalls.ErrorWithEvent("process_vm_readv", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/158"}), // TODO(b/260724654)
|
||||
271: syscalls.ErrorWithEvent("process_vm_writev", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/158"}), // TODO(b/260724654)
|
||||
|
||||
@@ -76,7 +76,7 @@ func Mount(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
|
||||
return 0, nil, err
|
||||
}
|
||||
var sourceTpop taskPathOperation
|
||||
sourceTpop, err = getTaskPathOperation(t, linux.AT_FDCWD, sourcePath, disallowEmptyPath, nofollowFinalSymlink)
|
||||
sourceTpop, err = getTaskPathOperation(t, linux.AT_FDCWD, sourcePath, disallowEmptyPath, followFinalSymlink)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user