From f3b0a527c2048fe14e1ce438b3f271c677a4fa12 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Fri, 13 Oct 2023 10:18:16 -0700 Subject: [PATCH] inet: allow to create abstract unix sockets in non-root namespaces PiperOrigin-RevId: 573253619 --- pkg/sentry/control/lifecycle.go | 21 +++--- pkg/sentry/control/proc.go | 29 ++++----- pkg/sentry/fsimpl/host/host.go | 2 +- pkg/sentry/fsimpl/testutil/kernel.go | 42 ++++++------ pkg/sentry/inet/BUILD | 4 ++ .../abstract_socket_namespace.go | 13 ++-- pkg/sentry/inet/namespace.go | 10 +++ pkg/sentry/kernel/BUILD | 3 +- pkg/sentry/kernel/kernel.go | 64 ++++++++----------- pkg/sentry/kernel/task.go | 10 --- pkg/sentry/kernel/task_clone.go | 37 ++++++----- pkg/sentry/kernel/task_start.go | 50 +++++++-------- pkg/sentry/socket/unix/unix.go | 47 +++++++------- runsc/boot/loader.go | 42 ++++++------ test/syscalls/linux/BUILD | 3 + .../linux/socket_unix_unbound_abstract.cc | 43 +++++++++++++ 16 files changed, 221 insertions(+), 199 deletions(-) rename pkg/sentry/{kernel => inet}/abstract_socket_namespace.go (95%) diff --git a/pkg/sentry/control/lifecycle.go b/pkg/sentry/control/lifecycle.go index 0ad94378b..554c205df 100644 --- a/pkg/sentry/control/lifecycle.go +++ b/pkg/sentry/control/lifecycle.go @@ -225,17 +225,16 @@ func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error { Filename: args.Filename, Argv: args.Argv, // Order Envv before SecretEnvv. - Envv: append(args.Envv, args.SecretEnvv...), - WorkingDirectory: args.WorkingDirectory, - Credentials: creds, - Umask: 0022, - Limits: ls, - MaxSymlinkTraversals: linux.MaxSymlinkTraversals, - UTSNamespace: l.Kernel.RootUTSNamespace(), - IPCNamespace: l.Kernel.RootIPCNamespace(), - AbstractSocketNamespace: l.Kernel.RootAbstractSocketNamespace(), - ContainerID: args.ContainerID, - PIDNamespace: pidNs, + Envv: append(args.Envv, args.SecretEnvv...), + WorkingDirectory: args.WorkingDirectory, + Credentials: creds, + Umask: 0022, + Limits: ls, + MaxSymlinkTraversals: linux.MaxSymlinkTraversals, + UTSNamespace: l.Kernel.RootUTSNamespace(), + IPCNamespace: l.Kernel.RootIPCNamespace(), + ContainerID: args.ContainerID, + PIDNamespace: pidNs, } ctx := initArgs.NewContext(l.Kernel) diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go index 7deef2b4b..68d9ed70c 100644 --- a/pkg/sentry/control/proc.go +++ b/pkg/sentry/control/proc.go @@ -198,21 +198,20 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI limitSet = limits.NewLimitSet() } initArgs := kernel.CreateProcessArgs{ - Filename: args.Filename, - Argv: args.Argv, - Envv: args.Envv, - WorkingDirectory: args.WorkingDirectory, - MountNamespace: args.MountNamespace, - Credentials: creds, - FDTable: fdTable, - Umask: 0022, - Limits: limitSet, - MaxSymlinkTraversals: linux.MaxSymlinkTraversals, - UTSNamespace: proc.Kernel.RootUTSNamespace(), - IPCNamespace: proc.Kernel.RootIPCNamespace(), - AbstractSocketNamespace: proc.Kernel.RootAbstractSocketNamespace(), - ContainerID: args.ContainerID, - PIDNamespace: pidns, + Filename: args.Filename, + Argv: args.Argv, + Envv: args.Envv, + WorkingDirectory: args.WorkingDirectory, + MountNamespace: args.MountNamespace, + Credentials: creds, + FDTable: fdTable, + Umask: 0022, + Limits: limitSet, + MaxSymlinkTraversals: linux.MaxSymlinkTraversals, + UTSNamespace: proc.Kernel.RootUTSNamespace(), + IPCNamespace: proc.Kernel.RootIPCNamespace(), + ContainerID: args.ContainerID, + PIDNamespace: pidns, } if initArgs.MountNamespace != nil { // initArgs must hold a reference on MountNamespace, which will diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go index 894e199dc..b64315ae6 100644 --- a/pkg/sentry/fsimpl/host/host.go +++ b/pkg/sentry/fsimpl/host/host.go @@ -656,7 +656,7 @@ func (i *inode) open(ctx context.Context, d *kernfs.Dentry, mnt *vfs.Mount, file return nil, err } // Currently, we only allow Unix sockets to be imported. - return unixsocket.NewFileDescription(ep, ep.Type(), flags, mnt, d.VFSDentry(), &i.locks) + return unixsocket.NewFileDescription(ep, ep.Type(), flags, nil, mnt, d.VFSDentry(), &i.locks) case unix.S_IFREG, unix.S_IFIFO, unix.S_IFCHR: if i.isTTY { diff --git a/pkg/sentry/fsimpl/testutil/kernel.go b/pkg/sentry/fsimpl/testutil/kernel.go index 1565fa591..f96a45c0b 100644 --- a/pkg/sentry/fsimpl/testutil/kernel.go +++ b/pkg/sentry/fsimpl/testutil/kernel.go @@ -91,15 +91,14 @@ func Boot() (*kernel.Kernel, error) { // Initiate the Kernel object, which is required by the Context passed // to createVFS in order to mount (among other things) procfs. if err = k.Init(kernel.InitKernelArgs{ - ApplicationCores: uint(runtime.GOMAXPROCS(-1)), - FeatureSet: cpuid.HostFeatureSet(), - Timekeeper: tk, - RootUserNamespace: creds.UserNamespace, - Vdso: vdso, - RootUTSNamespace: kernel.NewUTSNamespace("hostname", "domain", creds.UserNamespace), - RootIPCNamespace: kernel.NewIPCNamespace(creds.UserNamespace), - RootAbstractSocketNamespace: kernel.NewAbstractSocketNamespace(), - PIDNamespace: kernel.NewRootPIDNamespace(creds.UserNamespace), + ApplicationCores: uint(runtime.GOMAXPROCS(-1)), + FeatureSet: cpuid.HostFeatureSet(), + Timekeeper: tk, + RootUserNamespace: creds.UserNamespace, + Vdso: vdso, + RootUTSNamespace: kernel.NewUTSNamespace("hostname", "domain", creds.UserNamespace), + RootIPCNamespace: kernel.NewIPCNamespace(creds.UserNamespace), + PIDNamespace: kernel.NewRootPIDNamespace(creds.UserNamespace), }); err != nil { return nil, fmt.Errorf("initializing kernel: %v", err) } @@ -135,19 +134,18 @@ func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup, mntns creds := auth.CredentialsFromContext(ctx) config := &kernel.TaskConfig{ - Kernel: k, - ThreadGroup: tc, - TaskImage: &kernel.TaskImage{Name: name, MemoryManager: m}, - Credentials: auth.CredentialsFromContext(ctx), - NetworkNamespace: k.RootNetworkNamespace(), - AllowedCPUMask: sched.NewFullCPUSet(k.ApplicationCores()), - UTSNamespace: kernel.UTSNamespaceFromContext(ctx), - IPCNamespace: kernel.IPCNamespaceFromContext(ctx), - AbstractSocketNamespace: kernel.NewAbstractSocketNamespace(), - MountNamespace: mntns, - FSContext: kernel.NewFSContext(root, cwd, 0022), - FDTable: k.NewFDTable(), - UserCounters: k.GetUserCounters(creds.RealKUID), + Kernel: k, + ThreadGroup: tc, + TaskImage: &kernel.TaskImage{Name: name, MemoryManager: m}, + Credentials: auth.CredentialsFromContext(ctx), + NetworkNamespace: k.RootNetworkNamespace(), + AllowedCPUMask: sched.NewFullCPUSet(k.ApplicationCores()), + UTSNamespace: kernel.UTSNamespaceFromContext(ctx), + IPCNamespace: kernel.IPCNamespaceFromContext(ctx), + MountNamespace: mntns, + FSContext: kernel.NewFSContext(root, cwd, 0022), + FDTable: k.NewFDTable(), + UserCounters: k.GetUserCounters(creds.RealKUID), } config.NetworkNamespace.IncRef() t, err := k.TaskSet().NewTask(ctx, config) diff --git a/pkg/sentry/inet/BUILD b/pkg/sentry/inet/BUILD index 11f9ece67..313696519 100644 --- a/pkg/sentry/inet/BUILD +++ b/pkg/sentry/inet/BUILD @@ -21,6 +21,7 @@ go_template_instance( go_library( name = "inet", srcs = [ + "abstract_socket_namespace.go", "context.go", "inet.go", "namespace.go", @@ -34,6 +35,9 @@ go_library( "//pkg/refs", "//pkg/sentry/fsimpl/nsfs", "//pkg/sentry/kernel/auth", + "//pkg/sentry/socket/unix/transport", + "//pkg/sync", + "//pkg/syserr", "//pkg/tcpip", "//pkg/tcpip/stack", ], diff --git a/pkg/sentry/kernel/abstract_socket_namespace.go b/pkg/sentry/inet/abstract_socket_namespace.go similarity index 95% rename from pkg/sentry/kernel/abstract_socket_namespace.go rename to pkg/sentry/inet/abstract_socket_namespace.go index bc360d95d..177199a4a 100644 --- a/pkg/sentry/kernel/abstract_socket_namespace.go +++ b/pkg/sentry/inet/abstract_socket_namespace.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package kernel +package inet import ( "fmt" @@ -47,13 +47,6 @@ type AbstractSocketNamespace struct { endpoints map[string]abstractEndpoint } -// NewAbstractSocketNamespace returns a new AbstractSocketNamespace. -func NewAbstractSocketNamespace() *AbstractSocketNamespace { - return &AbstractSocketNamespace{ - endpoints: make(map[string]abstractEndpoint), - } -} - // A boundEndpoint wraps a transport.BoundEndpoint to maintain a reference on // its backing socket. type boundEndpoint struct { @@ -67,6 +60,10 @@ func (e *boundEndpoint) Release(ctx context.Context) { e.BoundEndpoint.Release(ctx) } +func (a *AbstractSocketNamespace) init() { + a.endpoints = make(map[string]abstractEndpoint) +} + // BoundEndpoint retrieves the endpoint bound to the given name. The return // value is nil if no endpoint was bound. func (a *AbstractSocketNamespace) BoundEndpoint(name string) transport.BoundEndpoint { diff --git a/pkg/sentry/inet/namespace.go b/pkg/sentry/inet/namespace.go index 5e006c6ae..d29e1db80 100644 --- a/pkg/sentry/inet/namespace.go +++ b/pkg/sentry/inet/namespace.go @@ -40,6 +40,9 @@ type Namespace struct { isRoot bool userNS *auth.UserNamespace + + // abstractSockets tracks abstract sockets that are in use. + abstractSockets AbstractSocketNamespace } // NewRootNamespace creates the root network namespace, with creator @@ -52,6 +55,7 @@ func NewRootNamespace(stack Stack, creator NetworkStackCreator, userNS *auth.Use isRoot: true, userNS: userNS, } + n.abstractSockets.init() return n } @@ -137,6 +141,7 @@ func (n *Namespace) init() { panic(err) } } + n.abstractSockets.init() } // afterLoad is invoked by stateify. @@ -144,6 +149,11 @@ func (n *Namespace) afterLoad() { n.init() } +// AbstractSockets returns AbstractSocketNamespace. +func (n *Namespace) AbstractSockets() *AbstractSocketNamespace { + return &n.abstractSockets +} + // NetworkStackCreator allows new instances of a network stack to be created. It // is used by the kernel to create new network namespaces when requested. type NetworkStackCreator interface { diff --git a/pkg/sentry/kernel/BUILD b/pkg/sentry/kernel/BUILD index 45d015001..5c627205a 100644 --- a/pkg/sentry/kernel/BUILD +++ b/pkg/sentry/kernel/BUILD @@ -1,6 +1,6 @@ +load("//pkg/sync/locking:locking.bzl", "declare_mutex", "declare_rwmutex") load("//tools:defs.bzl", "go_library", "go_test", "proto_library") load("//tools/go_generics:defs.bzl", "go_template_instance") -load("//pkg/sync/locking:locking.bzl", "declare_mutex", "declare_rwmutex") package( default_applicable_licenses = ["//:license"], @@ -225,7 +225,6 @@ proto_library( go_library( name = "kernel", srcs = [ - "abstract_socket_namespace.go", "aio.go", "atomicptr_bucket_slice_unsafe.go", "atomicptr_bucket_unsafe.go", diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index 261425b0b..1f94e124a 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -139,18 +139,17 @@ type Kernel struct { mf *pgalloc.MemoryFile `state:"nosave"` // See InitKernelArgs for the meaning of these fields. - featureSet cpuid.FeatureSet - timekeeper *Timekeeper - tasks *TaskSet - rootUserNamespace *auth.UserNamespace - rootNetworkNamespace *inet.Namespace - applicationCores uint - useHostCores bool - extraAuxv []arch.AuxEntry - vdso *loader.VDSO - rootUTSNamespace *UTSNamespace - rootIPCNamespace *IPCNamespace - rootAbstractSocketNamespace *AbstractSocketNamespace + featureSet cpuid.FeatureSet + timekeeper *Timekeeper + tasks *TaskSet + rootUserNamespace *auth.UserNamespace + rootNetworkNamespace *inet.Namespace + applicationCores uint + useHostCores bool + extraAuxv []arch.AuxEntry + vdso *loader.VDSO + rootUTSNamespace *UTSNamespace + rootIPCNamespace *IPCNamespace // futexes is the "root" futex.Manager, from which all others are forked. // This is necessary to ensure that shared futexes are coherent across all @@ -366,9 +365,6 @@ type InitKernelArgs struct { // RootIPCNamespace is the root IPC namespace. RootIPCNamespace *IPCNamespace - // RootAbstractSocketNamespace is the root Abstract Socket namespace. - RootAbstractSocketNamespace *AbstractSocketNamespace - // PIDNamespace is the root PID namespace. PIDNamespace *PIDNamespace } @@ -397,7 +393,6 @@ func (k *Kernel) Init(args InitKernelArgs) error { k.rootUserNamespace = args.RootUserNamespace k.rootUTSNamespace = args.RootUTSNamespace k.rootIPCNamespace = args.RootIPCNamespace - k.rootAbstractSocketNamespace = args.RootAbstractSocketNamespace k.rootNetworkNamespace = args.RootNetworkNamespace if k.rootNetworkNamespace == nil { k.rootNetworkNamespace = inet.NewRootNamespace(nil, nil, args.RootUserNamespace) @@ -727,9 +722,6 @@ type CreateProcessArgs struct { // PIDNamespace is the initial PID Namespace. PIDNamespace *PIDNamespace - // AbstractSocketNamespace is the initial Abstract Socket namespace. - AbstractSocketNamespace *AbstractSocketNamespace - // MountNamespace optionally contains the mount namespace for this // process. If nil, the init process's mount namespace is used. // @@ -937,21 +929,20 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID, // Create the task. config := &TaskConfig{ - Kernel: k, - ThreadGroup: tg, - TaskImage: image, - FSContext: fsContext, - FDTable: args.FDTable, - Credentials: args.Credentials, - NetworkNamespace: k.RootNetworkNamespace(), - AllowedCPUMask: sched.NewFullCPUSet(k.applicationCores), - UTSNamespace: args.UTSNamespace, - IPCNamespace: args.IPCNamespace, - AbstractSocketNamespace: args.AbstractSocketNamespace, - MountNamespace: mntns, - ContainerID: args.ContainerID, - InitialCgroups: args.InitialCgroups, - UserCounters: k.GetUserCounters(args.Credentials.RealKUID), + Kernel: k, + ThreadGroup: tg, + TaskImage: image, + FSContext: fsContext, + FDTable: args.FDTable, + Credentials: args.Credentials, + NetworkNamespace: k.RootNetworkNamespace(), + AllowedCPUMask: sched.NewFullCPUSet(k.applicationCores), + UTSNamespace: args.UTSNamespace, + IPCNamespace: args.IPCNamespace, + MountNamespace: mntns, + ContainerID: args.ContainerID, + InitialCgroups: args.InitialCgroups, + UserCounters: k.GetUserCounters(args.Credentials.RealKUID), // A task with no parent starts out with no session keyring. SessionKeyring: nil, } @@ -1324,11 +1315,6 @@ func (k *Kernel) RootPIDNamespace() *PIDNamespace { return k.tasks.Root } -// RootAbstractSocketNamespace returns the root AbstractSocketNamespace. -func (k *Kernel) RootAbstractSocketNamespace() *AbstractSocketNamespace { - return k.rootAbstractSocketNamespace -} - // RootNetworkNamespace returns the root network namespace, always non-nil. func (k *Kernel) RootNetworkNamespace() *inet.Namespace { return k.rootNetworkNamespace diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go index 06517f005..0e898a42b 100644 --- a/pkg/sentry/kernel/task.go +++ b/pkg/sentry/kernel/task.go @@ -441,11 +441,6 @@ type Task struct { // ipcns is protected by mu. ipcns is owned by the task goroutine. ipcns *IPCNamespace - // abstractSockets tracks abstract sockets that are in use. - // - // abstractSockets is protected by mu. - abstractSockets *AbstractSocketNamespace - // mountNamespace is the task's mount namespace. // // It is protected by mu. It is owned by the task goroutine. @@ -809,11 +804,6 @@ func (t *Task) GetMountNamespace() *vfs.MountNamespace { return mntns } -// AbstractSockets returns t's AbstractSocketNamespace. -func (t *Task) AbstractSockets() *AbstractSocketNamespace { - return t.abstractSockets -} - // ContainerID returns t's container ID. func (t *Task) ContainerID() string { return t.containerID diff --git a/pkg/sentry/kernel/task_clone.go b/pkg/sentry/kernel/task_clone.go index 23066317a..7131e083e 100644 --- a/pkg/sentry/kernel/task_clone.go +++ b/pkg/sentry/kernel/task_clone.go @@ -248,25 +248,24 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) { } cfg := &TaskConfig{ - Kernel: t.k, - ThreadGroup: tg, - SignalMask: t.SignalMask(), - TaskImage: image, - FSContext: fsContext, - FDTable: fdTable, - Credentials: creds, - Niceness: t.Niceness(), - NetworkNamespace: netns, - AllowedCPUMask: t.CPUMask(), - UTSNamespace: utsns, - IPCNamespace: ipcns, - AbstractSocketNamespace: t.abstractSockets, - MountNamespace: mntns, - RSeqAddr: rseqAddr, - RSeqSignature: rseqSignature, - ContainerID: t.ContainerID(), - UserCounters: uc, - SessionKeyring: sessionKeyring, + Kernel: t.k, + ThreadGroup: tg, + SignalMask: t.SignalMask(), + TaskImage: image, + FSContext: fsContext, + FDTable: fdTable, + Credentials: creds, + Niceness: t.Niceness(), + NetworkNamespace: netns, + AllowedCPUMask: t.CPUMask(), + UTSNamespace: utsns, + IPCNamespace: ipcns, + MountNamespace: mntns, + RSeqAddr: rseqAddr, + RSeqSignature: rseqSignature, + ContainerID: t.ContainerID(), + UserCounters: uc, + SessionKeyring: sessionKeyring, } if args.Flags&linux.CLONE_THREAD == 0 { cfg.Parent = t diff --git a/pkg/sentry/kernel/task_start.go b/pkg/sentry/kernel/task_start.go index fb7cbefb0..2ac76b4c2 100644 --- a/pkg/sentry/kernel/task_start.go +++ b/pkg/sentry/kernel/task_start.go @@ -81,9 +81,6 @@ type TaskConfig struct { // IPCNamespace is the IPCNamespace of the new task. IPCNamespace *IPCNamespace - // AbstractSocketNamespace is the AbstractSocketNamespace of the new task. - AbstractSocketNamespace *AbstractSocketNamespace - // MountNamespace is the MountNamespace of the new task. MountNamespace *vfs.MountNamespace @@ -152,30 +149,29 @@ func (ts *TaskSet) newTask(ctx context.Context, cfg *TaskConfig) (*Task, error) parent: cfg.Parent, children: make(map[*Task]struct{}), }, - runState: (*runApp)(nil), - interruptChan: make(chan struct{}, 1), - signalMask: atomicbitops.FromUint64(uint64(cfg.SignalMask)), - signalStack: linux.SignalStack{Flags: linux.SS_DISABLE}, - image: *image, - fsContext: cfg.FSContext, - fdTable: cfg.FDTable, - k: cfg.Kernel, - ptraceTracees: make(map[*Task]struct{}), - allowedCPUMask: cfg.AllowedCPUMask.Copy(), - ioUsage: &usage.IO{}, - niceness: cfg.Niceness, - utsns: cfg.UTSNamespace, - ipcns: cfg.IPCNamespace, - abstractSockets: cfg.AbstractSocketNamespace, - mountNamespace: cfg.MountNamespace, - rseqCPU: -1, - rseqAddr: cfg.RSeqAddr, - rseqSignature: cfg.RSeqSignature, - futexWaiter: futex.NewWaiter(), - containerID: cfg.ContainerID, - cgroups: make(map[Cgroup]struct{}), - userCounters: cfg.UserCounters, - sessionKeyring: cfg.SessionKeyring, + runState: (*runApp)(nil), + interruptChan: make(chan struct{}, 1), + signalMask: atomicbitops.FromUint64(uint64(cfg.SignalMask)), + signalStack: linux.SignalStack{Flags: linux.SS_DISABLE}, + image: *image, + fsContext: cfg.FSContext, + fdTable: cfg.FDTable, + k: cfg.Kernel, + ptraceTracees: make(map[*Task]struct{}), + allowedCPUMask: cfg.AllowedCPUMask.Copy(), + ioUsage: &usage.IO{}, + niceness: cfg.Niceness, + utsns: cfg.UTSNamespace, + ipcns: cfg.IPCNamespace, + mountNamespace: cfg.MountNamespace, + rseqCPU: -1, + rseqAddr: cfg.RSeqAddr, + rseqSignature: cfg.RSeqSignature, + futexWaiter: futex.NewWaiter(), + containerID: cfg.ContainerID, + cgroups: make(map[Cgroup]struct{}), + userCounters: cfg.UserCounters, + sessionKeyring: cfg.SessionKeyring, } t.netns = cfg.NetworkNamespace t.creds.Store(cfg.Credentials) diff --git a/pkg/sentry/socket/unix/unix.go b/pkg/sentry/socket/unix/unix.go index 45896d123..a212e8fce 100644 --- a/pkg/sentry/socket/unix/unix.go +++ b/pkg/sentry/socket/unix/unix.go @@ -30,6 +30,7 @@ import ( "gvisor.dev/gvisor/pkg/marshal" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs" + "gvisor.dev/gvisor/pkg/sentry/inet" "gvisor.dev/gvisor/pkg/sentry/kernel" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" "gvisor.dev/gvisor/pkg/sentry/socket" @@ -54,14 +55,15 @@ type Socket struct { socket.SendReceiveTimeout socketRefs - ep transport.Endpoint - stype linux.SockType + namespace *inet.Namespace + ep transport.Endpoint + stype linux.SockType // abstractName and abstractNamespace indicate the name and namespace of the // socket if it is bound to an abstract socket namespace. Once the socket is // bound, they cannot be modified. - abstractName string - abstractNamespace *kernel.AbstractSocketNamespace + abstractName string + abstractBound bool } var _ = socket.Socket(&Socket{}) @@ -73,8 +75,10 @@ func NewSockfsFile(t *kernel.Task, ep transport.Endpoint, stype linux.SockType) d := sockfs.NewDentry(t, mnt) defer d.DecRef(t) - fd, err := NewFileDescription(ep, stype, linux.O_RDWR, mnt, d, &vfs.FileLocks{}) + ns := t.GetNetworkNamespace() + fd, err := NewFileDescription(ep, stype, linux.O_RDWR, ns, mnt, d, &vfs.FileLocks{}) if err != nil { + ns.DecRef(t) return nil, syserr.FromError(err) } return fd, nil @@ -82,7 +86,7 @@ func NewSockfsFile(t *kernel.Task, ep transport.Endpoint, stype linux.SockType) // NewFileDescription creates and returns a socket file description // corresponding to the given mount and dentry. -func NewFileDescription(ep transport.Endpoint, stype linux.SockType, flags uint32, mnt *vfs.Mount, d *vfs.Dentry, locks *vfs.FileLocks) (*vfs.FileDescription, error) { +func NewFileDescription(ep transport.Endpoint, stype linux.SockType, flags uint32, ns *inet.Namespace, mnt *vfs.Mount, d *vfs.Dentry, locks *vfs.FileLocks) (*vfs.FileDescription, error) { // You can create AF_UNIX, SOCK_RAW sockets. They're the same as // SOCK_DGRAM and don't require CAP_NET_RAW. if stype == linux.SOCK_RAW { @@ -90,8 +94,9 @@ func NewFileDescription(ep transport.Endpoint, stype linux.SockType, flags uint3 } sock := &Socket{ - ep: ep, - stype: stype, + ep: ep, + stype: stype, + namespace: ns, } sock.InitRefs() sock.LockFD.Init(locks) @@ -111,8 +116,11 @@ func (s *Socket) DecRef(ctx context.Context) { s.socketRefs.DecRef(func() { kernel.KernelFromContext(ctx).DeleteSocket(&s.vfsfd) s.ep.Close(ctx) - if s.abstractNamespace != nil { - s.abstractNamespace.Remove(s.abstractName, s) + if s.abstractBound { + s.namespace.AbstractSockets().Remove(s.abstractName, s) + } + if s.namespace != nil { + s.namespace.DecRef(ctx) } }) } @@ -214,10 +222,7 @@ func (s *Socket) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { // If path is empty, the socket is autobound to an abstract address. if len(p) == 0 || p[0] == 0 { // Abstract socket. See net/unix/af_unix.c:unix_bind_abstract(). - if t.IsNetworkNamespaced() { - return syserr.ErrInvalidEndpointState - } - asn := t.AbstractSockets() + asn := s.namespace.AbstractSockets() p, err := asn.Bind(t, p, bep, s) if err != nil { return err @@ -229,7 +234,7 @@ func (s *Socket) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { } // The socket has been successfully bound. We can update the following. s.abstractName = name - s.abstractNamespace = asn + s.abstractBound = true return nil } @@ -518,7 +523,7 @@ func (s *Socket) Listen(t *kernel.Task, backlog int) *syserr.Error { // extractEndpoint retrieves the transport.BoundEndpoint associated with a Unix // socket path. The Release must be called on the transport.BoundEndpoint when // the caller is done with it. -func extractEndpoint(t *kernel.Task, sockaddr []byte) (transport.BoundEndpoint, *syserr.Error) { +func (s *Socket) extractEndpoint(t *kernel.Task, sockaddr []byte) (transport.BoundEndpoint, *syserr.Error) { path, err := extractPath(sockaddr) if err != nil { return nil, err @@ -530,11 +535,7 @@ func extractEndpoint(t *kernel.Task, sockaddr []byte) (transport.BoundEndpoint, // Is it abstract? if path[0] == 0 { - if t.IsNetworkNamespaced() { - return nil, syserr.ErrInvalidArgument - } - - ep := t.AbstractSockets().BoundEndpoint(path[1:]) + ep := s.namespace.AbstractSockets().BoundEndpoint(path[1:]) if ep == nil { // No socket found. return nil, syserr.ErrConnectionRefused @@ -569,7 +570,7 @@ func extractEndpoint(t *kernel.Task, sockaddr []byte) (transport.BoundEndpoint, // Connect implements the linux syscall connect(2) for unix sockets. func (s *Socket) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error { - ep, err := extractEndpoint(t, sockaddr) + ep, err := s.extractEndpoint(t, sockaddr) if err != nil { return err } @@ -609,7 +610,7 @@ func (s *Socket) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flag } return 0, syserr.ErrNotSupported default: - ep, err := extractEndpoint(t, to) + ep, err := s.extractEndpoint(t, to) if err != nil { return 0, err } diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 851c6dc16..23ac4eaf3 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -441,16 +441,15 @@ func New(args Args) (*Loader, error) { // Initiate the Kernel object, which is required by the Context passed // to createVFS in order to mount (among other things) procfs. if err = k.Init(kernel.InitKernelArgs{ - FeatureSet: cpuid.HostFeatureSet().Fixed(), - Timekeeper: tk, - RootUserNamespace: creds.UserNamespace, - RootNetworkNamespace: netns, - ApplicationCores: uint(args.NumCPU), - Vdso: vdso, - RootUTSNamespace: kernel.NewUTSNamespace(args.Spec.Hostname, args.Spec.Hostname, creds.UserNamespace), - RootIPCNamespace: kernel.NewIPCNamespace(creds.UserNamespace), - RootAbstractSocketNamespace: kernel.NewAbstractSocketNamespace(), - PIDNamespace: kernel.NewRootPIDNamespace(creds.UserNamespace), + FeatureSet: cpuid.HostFeatureSet().Fixed(), + Timekeeper: tk, + RootUserNamespace: creds.UserNamespace, + RootNetworkNamespace: netns, + ApplicationCores: uint(args.NumCPU), + Vdso: vdso, + RootUTSNamespace: kernel.NewUTSNamespace(args.Spec.Hostname, args.Spec.Hostname, creds.UserNamespace), + RootIPCNamespace: kernel.NewIPCNamespace(creds.UserNamespace), + PIDNamespace: kernel.NewRootPIDNamespace(creds.UserNamespace), }); err != nil { return nil, fmt.Errorf("initializing kernel: %w", err) } @@ -559,18 +558,17 @@ func createProcessArgs(id string, spec *specs.Spec, creds *auth.Credentials, k * // Create the process arguments. procArgs := kernel.CreateProcessArgs{ - Argv: spec.Process.Args, - Envv: env, - WorkingDirectory: wd, - Credentials: creds, - Umask: 0022, - Limits: ls, - MaxSymlinkTraversals: linux.MaxSymlinkTraversals, - UTSNamespace: k.RootUTSNamespace(), - IPCNamespace: k.RootIPCNamespace(), - AbstractSocketNamespace: k.RootAbstractSocketNamespace(), - ContainerID: id, - PIDNamespace: pidns, + Argv: spec.Process.Args, + Envv: env, + WorkingDirectory: wd, + Credentials: creds, + Umask: 0022, + Limits: ls, + MaxSymlinkTraversals: linux.MaxSymlinkTraversals, + UTSNamespace: k.RootUTSNamespace(), + IPCNamespace: k.RootIPCNamespace(), + ContainerID: id, + PIDNamespace: pidns, } return procArgs, nil diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index db374ae69..a40681a9c 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -3673,6 +3673,9 @@ cc_binary( ":unix_domain_socket_test_util", "//test/util:socket_util", gtest, + "//test/util:capability_util", + "//test/util:cleanup", + "//test/util:file_descriptor", "//test/util:test_main", "//test/util:test_util", ], diff --git a/test/syscalls/linux/socket_unix_unbound_abstract.cc b/test/syscalls/linux/socket_unix_unbound_abstract.cc index 04d263d0c..5eada3117 100644 --- a/test/syscalls/linux/socket_unix_unbound_abstract.cc +++ b/test/syscalls/linux/socket_unix_unbound_abstract.cc @@ -13,12 +13,17 @@ // limitations under the License. #include +#include #include #include #include +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/linux_capability_util.h" #include "test/util/socket_util.h" #include "test/util/test_util.h" @@ -118,6 +123,44 @@ TEST_P(UnboundAbstractUnixSocketPairTest, AutoBindAddrInUse) { SyscallFailsWithErrno(EADDRINUSE)); } +TEST_P(UnboundAbstractUnixSocketPairTest, BindConnectInSubNamespace) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + + const FileDescriptor ns = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/ns/net", O_RDONLY)); + auto cleanup = + Cleanup([&ns] { ASSERT_THAT(setns(ns.get(), 0), SyscallSucceeds()); }); + ASSERT_THAT(unshare(CLONE_NEWNET), SyscallSucceeds()); + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(unshare(CLONE_NEWNET), SyscallSucceeds()); + + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + ASSERT_THAT( + bind(sockets->first_fd(), reinterpret_cast(&addr), + sizeof(sa_family_t)), + SyscallSucceeds()); + socklen_t addr_len = sizeof(addr); + ASSERT_THAT(getsockname(sockets->first_fd(), + reinterpret_cast(&addr), &addr_len), + SyscallSucceeds()); + if ((GetParam().type & SOCK_DGRAM) == 0) { + ASSERT_THAT(listen(sockets->first_fd(), 1 /* backlog */), + SyscallSucceeds()); + } + EXPECT_THAT(connect(sockets->second_fd(), + reinterpret_cast(&addr), addr_len), + SyscallSucceeds()); + + auto socketsInSubNS = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + EXPECT_THAT(connect(socketsInSubNS->second_fd(), + reinterpret_cast(&addr), addr_len), + SyscallFailsWithErrno(ECONNREFUSED)); + EXPECT_THAT(bind(socketsInSubNS->first_fd(), + reinterpret_cast(&addr), addr_len), + SyscallSucceeds()); +} + TEST_P(UnboundAbstractUnixSocketPairTest, ListenZeroBacklog) { SKIP_IF((GetParam().type & SOCK_DGRAM) != 0); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());