mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
inet: allow to create abstract unix sockets in non-root namespaces
PiperOrigin-RevId: 573253619
This commit is contained in:
@@ -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)
|
||||
|
||||
+14
-15
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
+5
-8
@@ -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 {
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
+25
-39
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+20
-22
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -13,12 +13,17 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#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<struct sockaddr*>(&addr),
|
||||
sizeof(sa_family_t)),
|
||||
SyscallSucceeds());
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
ASSERT_THAT(getsockname(sockets->first_fd(),
|
||||
reinterpret_cast<struct sockaddr*>(&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<struct sockaddr*>(&addr), addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
auto socketsInSubNS = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
EXPECT_THAT(connect(socketsInSubNS->second_fd(),
|
||||
reinterpret_cast<struct sockaddr*>(&addr), addr_len),
|
||||
SyscallFailsWithErrno(ECONNREFUSED));
|
||||
EXPECT_THAT(bind(socketsInSubNS->first_fd(),
|
||||
reinterpret_cast<struct sockaddr*>(&addr), addr_len),
|
||||
SyscallSucceeds());
|
||||
}
|
||||
|
||||
TEST_P(UnboundAbstractUnixSocketPairTest, ListenZeroBacklog) {
|
||||
SKIP_IF((GetParam().type & SOCK_DGRAM) != 0);
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
Reference in New Issue
Block a user