diff --git a/pkg/sentry/fs/host/socket.go b/pkg/sentry/fs/host/socket.go index 54c421775..458e565db 100644 --- a/pkg/sentry/fs/host/socket.go +++ b/pkg/sentry/fs/host/socket.go @@ -156,9 +156,7 @@ func NewSocketWithDirent(ctx context.Context, d *fs.Dirent, f *fd.FD, flags fs.F f.Release() e.Init() - - ep := transport.NewExternal(ctx, e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e) - + ep := transport.NewExternal(e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e) return unixsocket.NewWithDirent(ctx, d, ep, e.stype, flags), nil } @@ -188,9 +186,7 @@ func newSocket(ctx context.Context, orgfd int, saveable bool) (*fs.File, error) e.srfd = srfd e.Init() - - ep := transport.NewExternal(ctx, e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e) - + ep := transport.NewExternal(e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e) return unixsocket.New(ctx, ep, e.stype), nil } @@ -397,4 +393,4 @@ func (c *ConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) { return atomic.LoadInt64(&c.sndbuf) } -// LINT.ThenChange(../../fsimpl/host/socket.go) +// LINT.ThenChange(../../socket/unix/transport/host.go) diff --git a/pkg/sentry/fsimpl/gofer/socket.go b/pkg/sentry/fsimpl/gofer/socket.go index b66ebb5a4..628ab3262 100644 --- a/pkg/sentry/fsimpl/gofer/socket.go +++ b/pkg/sentry/fsimpl/gofer/socket.go @@ -20,7 +20,6 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/p9" - "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" "gvisor.dev/gvisor/pkg/sentry/socket/unix/transport" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/waiter" @@ -106,14 +105,14 @@ func (e *endpoint) UnidirectionalConnect(ctx context.Context) (transport.Connect return c, nil } -func (e *endpoint) newConnectedEndpoint(ctx context.Context, sockType linux.SockType, queue *waiter.Queue) (*host.SCMConnectedEndpoint, *syserr.Error) { +func (e *endpoint) newConnectedEndpoint(ctx context.Context, sockType linux.SockType, queue *waiter.Queue) (*transport.SCMConnectedEndpoint, *syserr.Error) { if e.dentry.fs.opts.lisaEnabled { hostSockFD, err := e.dentry.controlFDLisa.Connect(ctx, sockType) if err != nil { return nil, syserr.ErrConnectionRefused } - c, serr := host.NewSCMEndpoint(ctx, hostSockFD, queue, e.path) + c, serr := transport.NewSCMEndpoint(hostSockFD, queue, e.path) if serr != nil { unix.Close(hostSockFD) log.Warningf("Gofer returned invalid host socket for BidirectionalConnect; file %+v sockType %d: %v", e.dentry.file, sockType, serr) @@ -131,7 +130,7 @@ func (e *endpoint) newConnectedEndpoint(ctx context.Context, sockType linux.Sock return nil, syserr.ErrConnectionRefused } - c, serr := host.NewSCMEndpoint(ctx, hostFile.FD(), queue, e.path) + c, serr := transport.NewSCMEndpoint(hostFile.FD(), queue, e.path) if serr != nil { hostFile.Close() log.Warningf("Gofer returned invalid host socket for BidirectionalConnect; file %+v sockType %d: %v", e.dentry.file, sockType, serr) diff --git a/pkg/sentry/fsimpl/host/BUILD b/pkg/sentry/fsimpl/host/BUILD index cd30ebbe2..03e3e684d 100644 --- a/pkg/sentry/fsimpl/host/BUILD +++ b/pkg/sentry/fsimpl/host/BUILD @@ -14,30 +14,14 @@ go_template_instance( }, ) -go_template_instance( - name = "connected_endpoint_refs", - out = "connected_endpoint_refs.go", - package = "host", - prefix = "ConnectedEndpoint", - template = "//pkg/refsvfs2:refs_template", - types = { - "T": "ConnectedEndpoint", - }, -) - go_library( name = "host", srcs = [ - "connected_endpoint_refs.go", - "control.go", "host.go", "host_unsafe.go", "inode_refs.go", "ioctl_unsafe.go", "save_restore.go", - "socket.go", - "socket_iovec.go", - "socket_unsafe.go", "tty.go", "util.go", "util_unsafe.go", diff --git a/pkg/sentry/fsimpl/host/control.go b/pkg/sentry/fsimpl/host/control.go deleted file mode 100644 index 1680d456e..000000000 --- a/pkg/sentry/fsimpl/host/control.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2020 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 host - -import ( - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/sentry/kernel" - "gvisor.dev/gvisor/pkg/sentry/kernel/auth" - "gvisor.dev/gvisor/pkg/sentry/socket/control" - "gvisor.dev/gvisor/pkg/sentry/socket/unix/transport" - "gvisor.dev/gvisor/pkg/sentry/vfs" -) - -type scmRights struct { - fds []int -} - -func newSCMRights(fds []int) control.SCMRightsVFS2 { - return &scmRights{fds} -} - -// Files implements control.SCMRights.Files. -func (c *scmRights) Files(ctx context.Context, max int) (control.RightsFilesVFS2, bool) { - n := max - var trunc bool - if l := len(c.fds); n > l { - n = l - } else if n < l { - trunc = true - } - - rf := control.RightsFilesVFS2(fdsToFiles(ctx, c.fds[:n])) - - // Only consume converted FDs (fdsToFiles may convert fewer than n FDs). - c.fds = c.fds[len(rf):] - return rf, trunc -} - -// Clone implements transport.RightsControlMessage.Clone. -func (c *scmRights) Clone() transport.RightsControlMessage { - // Host rights never need to be cloned. - return nil -} - -// Release implements transport.RightsControlMessage.Release. -func (c *scmRights) Release(ctx context.Context) { - for _, fd := range c.fds { - unix.Close(fd) - } - c.fds = nil -} - -// If an error is encountered, only files created before the error will be -// returned. This is what Linux does. -func fdsToFiles(ctx context.Context, fds []int) []*vfs.FileDescription { - files := make([]*vfs.FileDescription, 0, len(fds)) - for _, fd := range fds { - // Get flags. We do it here because they may be modified - // by subsequent functions. - fileFlags, _, errno := unix.Syscall(unix.SYS_FCNTL, uintptr(fd), unix.F_GETFL, 0) - if errno != 0 { - ctx.Warningf("Error retrieving host FD flags: %v", error(errno)) - break - } - - // Create the file backed by hostFD. - file, err := NewFD(ctx, kernel.KernelFromContext(ctx).HostMount(), fd, &NewFDOptions{}) - if err != nil { - ctx.Warningf("Error creating file from host FD: %v", err) - break - } - - if err := file.SetStatusFlags(ctx, auth.CredentialsFromContext(ctx), uint32(fileFlags&linux.O_NONBLOCK)); err != nil { - ctx.Warningf("Error setting flags on host FD file: %v", err) - break - } - - files = append(files, file) - } - return files -} diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go index a27513103..f1939776e 100644 --- a/pkg/sentry/fsimpl/host/host.go +++ b/pkg/sentry/fsimpl/host/host.go @@ -36,6 +36,8 @@ import ( "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/memmap" unixsocket "gvisor.dev/gvisor/pkg/sentry/socket/unix" + "gvisor.dev/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.dev/gvisor/pkg/sentry/uniqueid" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/usermem" @@ -627,6 +629,19 @@ func (i *inode) open(ctx context.Context, d *kernfs.Dentry, mnt *vfs.Mount, file } } +// Create a new host-backed endpoint from the given fd and its corresponding +// notification queue. +func newEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue) (transport.Endpoint, error) { + // Set up an external transport.Endpoint using the host fd. + addr := fmt.Sprintf("hostfd:[%d]", hostFD) + e, err := transport.NewHostConnectedEndpoint(hostFD, addr) + if err != nil { + return nil, err.ToError() + } + ep := transport.NewExternal(e.SockType(), uniqueid.GlobalProviderFromContext(ctx), queue, e, e) + return ep, nil +} + // fileDescription is embedded by host fd implementations of FileDescriptionImpl. // // +stateify savable diff --git a/pkg/sentry/fsimpl/host/save_restore.go b/pkg/sentry/fsimpl/host/save_restore.go index c502d8e99..31301c715 100644 --- a/pkg/sentry/fsimpl/host/save_restore.go +++ b/pkg/sentry/fsimpl/host/save_restore.go @@ -68,10 +68,3 @@ func (i *inode) afterLoad() { } } } - -// afterLoad is invoked by stateify. -func (c *ConnectedEndpoint) afterLoad() { - if err := c.initFromOptions(); err != nil { - panic(fmt.Sprintf("initFromOptions failed: %v", err)) - } -} diff --git a/pkg/sentry/socket/control/control_vfs2.go b/pkg/sentry/socket/control/control_vfs2.go index a638cb955..dcc2cdda8 100644 --- a/pkg/sentry/socket/control/control_vfs2.go +++ b/pkg/sentry/socket/control/control_vfs2.go @@ -126,11 +126,3 @@ func PackRightsVFS2(t *kernel.Task, rights SCMRightsVFS2, cloexec bool, buf []by align := t.Arch().Width() return putCmsg(buf, flags, linux.SCM_RIGHTS, align, fds) } - -// NewVFS2 creates default control messages if needed. -func NewVFS2(t *kernel.Task, socketOrEndpoint interface{}, rights SCMRightsVFS2) transport.ControlMessages { - return transport.ControlMessages{ - Credentials: makeCreds(t, socketOrEndpoint), - Rights: rights, - } -} diff --git a/pkg/sentry/socket/unix/transport/BUILD b/pkg/sentry/socket/unix/transport/BUILD index 0d11bb251..1df738d24 100644 --- a/pkg/sentry/socket/unix/transport/BUILD +++ b/pkg/sentry/socket/unix/transport/BUILD @@ -26,6 +26,17 @@ go_template_instance( }, ) +go_template_instance( + name = "host_connected_endpoint_refs", + out = "host_connected_endpoint_refs.go", + package = "transport", + prefix = "HostConnectedEndpoint", + template = "//pkg/refsvfs2:refs_template", + types = { + "T": "HostConnectedEndpoint", + }, +) + go_library( name = "transport", srcs = [ @@ -33,8 +44,13 @@ go_library( "connectioned_state.go", "connectionless.go", "connectionless_state.go", + "host.go", + "host_connected_endpoint_refs.go", + "host_iovec.go", + "host_unsafe.go", "queue.go", "queue_refs.go", + "save_restore.go", "transport_message_list.go", "unix.go", ], @@ -42,15 +58,21 @@ go_library( deps = [ "//pkg/abi/linux", "//pkg/context", + "//pkg/errors/linuxerr", + "//pkg/fdnotifier", "//pkg/ilist", "//pkg/log", "//pkg/refs", "//pkg/refsvfs2", + "//pkg/sentry/hostfd", "//pkg/sentry/inet", + "//pkg/sentry/uniqueid", "//pkg/sync", "//pkg/syserr", "//pkg/tcpip", "//pkg/tcpip/buffer", + "//pkg/unet", "//pkg/waiter", + "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/pkg/sentry/socket/unix/transport/connectioned.go b/pkg/sentry/socket/unix/transport/connectioned.go index dc6bc78fd..673c80a60 100644 --- a/pkg/sentry/socket/unix/transport/connectioned.go +++ b/pkg/sentry/socket/unix/transport/connectioned.go @@ -17,19 +17,13 @@ package transport import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/sentry/uniqueid" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/waiter" ) -// UniqueIDProvider generates a sequence of unique identifiers useful for, -// among other things, lock ordering. -type UniqueIDProvider interface { - // UniqueID returns a new unique identifier. - UniqueID() uint64 -} - // A ConnectingEndpoint is a connectioned unix endpoint that is attempting to // establish a bidirectional connection with a BoundEndpoint. type ConnectingEndpoint interface { @@ -96,7 +90,7 @@ type connectionedEndpoint struct { id uint64 // idGenerator is used to generate new unique endpoint identifiers. - idGenerator UniqueIDProvider + idGenerator uniqueid.Provider // stype is used by connecting sockets to ensure that they are the // same type. The value is typically either tcpip.SockSeqpacket or @@ -117,11 +111,11 @@ var ( ) // NewConnectioned creates a new unbound connectionedEndpoint. -func NewConnectioned(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) Endpoint { +func NewConnectioned(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) Endpoint { return newConnectioned(ctx, stype, uid) } -func newConnectioned(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) *connectionedEndpoint { +func newConnectioned(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) *connectionedEndpoint { ep := &connectionedEndpoint{ baseEndpoint: baseEndpoint{Queue: &waiter.Queue{}}, id: uid.UniqueID(), @@ -136,7 +130,7 @@ func newConnectioned(ctx context.Context, stype linux.SockType, uid UniqueIDProv } // NewPair allocates a new pair of connected unix-domain connectionedEndpoints. -func NewPair(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) (Endpoint, Endpoint) { +func NewPair(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) (Endpoint, Endpoint) { a := newConnectioned(ctx, stype, uid) b := newConnectioned(ctx, stype, uid) @@ -169,7 +163,7 @@ func NewPair(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) (E // NewExternal creates a new externally backed Endpoint. It behaves like a // socketpair. -func NewExternal(ctx context.Context, stype linux.SockType, uid UniqueIDProvider, queue *waiter.Queue, receiver Receiver, connected ConnectedEndpoint) Endpoint { +func NewExternal(stype linux.SockType, uid uniqueid.Provider, queue *waiter.Queue, receiver Receiver, connected ConnectedEndpoint) Endpoint { ep := &connectionedEndpoint{ baseEndpoint: baseEndpoint{Queue: queue, receiver: receiver, connected: connected}, id: uid.UniqueID(), diff --git a/pkg/sentry/fsimpl/host/socket.go b/pkg/sentry/socket/unix/transport/host.go similarity index 57% rename from pkg/sentry/fsimpl/host/socket.go rename to pkg/sentry/socket/unix/transport/host.go index 709d5747d..df078da46 100644 --- a/pkg/sentry/fsimpl/host/socket.go +++ b/pkg/sentry/socket/unix/transport/host.go @@ -1,4 +1,4 @@ -// Copyright 2020 The gVisor Authors. +// Copyright 2021 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. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package host +package transport import ( "fmt" @@ -24,9 +24,6 @@ import ( "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/fdnotifier" "gvisor.dev/gvisor/pkg/log" - "gvisor.dev/gvisor/pkg/sentry/socket/control" - "gvisor.dev/gvisor/pkg/sentry/socket/unix/transport" - "gvisor.dev/gvisor/pkg/sentry/uniqueid" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" @@ -34,32 +31,37 @@ import ( "gvisor.dev/gvisor/pkg/waiter" ) -// Create a new host-backed endpoint from the given fd and its corresponding -// notification queue. -func newEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue) (transport.Endpoint, error) { - // Set up an external transport.Endpoint using the host fd. - addr := fmt.Sprintf("hostfd:[%d]", hostFD) - e, err := NewConnectedEndpoint(hostFD, addr) - if err != nil { - return nil, err.ToError() - } - ep := transport.NewExternal(ctx, e.stype, uniqueid.GlobalProviderFromContext(ctx), queue, e, e) - return ep, nil +// SCMRights implements RightsControlMessage with host FDs. +type SCMRights struct { + FDs []int } -// ConnectedEndpoint is an implementation of transport.ConnectedEndpoint and -// transport.Receiver. It is backed by a host fd that was imported at sentry -// startup. This fd is shared with a hostfs inode, which retains ownership of -// it. +// Clone implements RightsControlMessage.Clone. +func (c *SCMRights) Clone() RightsControlMessage { + // Host rights never need to be cloned. + return nil +} + +// Release implements RightsControlMessage.Release. +func (c *SCMRights) Release(ctx context.Context) { + for _, fd := range c.FDs { + unix.Close(fd) + } + c.FDs = nil +} + +// HostConnectedEndpoint is an implementation of ConnectedEndpoint and +// Receiver. It is backed by a host fd that was imported at sentry startup. +// This fd is shared with a hostfs inode, which retains ownership of it. // -// ConnectedEndpoint is saveable, since we expect that the host will provide -// the same fd upon restore. +// HostConnectedEndpoint is saveable, since we expect that the host will +// provide the same fd upon restore. // // As of this writing, we only allow Unix sockets to be imported. // // +stateify savable -type ConnectedEndpoint struct { - ConnectedEndpointRefs +type HostConnectedEndpoint struct { + HostConnectedEndpointRefs // mu protects fd below. mu sync.RWMutex `state:"nosave"` @@ -82,14 +84,14 @@ type ConnectedEndpoint struct { stype linux.SockType } -// init performs initialization required for creating new ConnectedEndpoints and -// for restoring them. -func (c *ConnectedEndpoint) init() *syserr.Error { +// init performs initialization required for creating new +// HostConnectedEndpoints and for restoring them. +func (c *HostConnectedEndpoint) init() *syserr.Error { c.InitRefs() return c.initFromOptions() } -func (c *ConnectedEndpoint) initFromOptions() *syserr.Error { +func (c *HostConnectedEndpoint) initFromOptions() *syserr.Error { family, err := unix.GetsockoptInt(c.fd, unix.SOL_SOCKET, unix.SO_DOMAIN) if err != nil { return syserr.FromError(err) @@ -120,14 +122,14 @@ func (c *ConnectedEndpoint) initFromOptions() *syserr.Error { return nil } -// NewConnectedEndpoint creates a new ConnectedEndpoint backed by a host fd -// imported at sentry startup, +// NewHostConnectedEndpoint creates a new HostConnectedEndpoint backed by a +// host fd imported at sentry startup. // -// The caller is responsible for calling Init(). Additionaly, Release needs to -// be called twice because ConnectedEndpoint is both a transport.Receiver and -// transport.ConnectedEndpoint. -func NewConnectedEndpoint(hostFD int, addr string) (*ConnectedEndpoint, *syserr.Error) { - e := ConnectedEndpoint{ +// The caller is responsible for calling Init(). Additionally, Release needs to +// be called twice because HostConnectedEndpoint is both a Receiver and +// HostConnectedEndpoint. +func NewHostConnectedEndpoint(hostFD int, addr string) (*HostConnectedEndpoint, *syserr.Error) { + e := HostConnectedEndpoint{ fd: hostFD, addr: addr, } @@ -136,13 +138,18 @@ func NewConnectedEndpoint(hostFD int, addr string) (*ConnectedEndpoint, *syserr. return nil, err } - // ConnectedEndpointRefs start off with a single reference. We need two. + // HostConnectedEndpointRefs start off with a single reference. We need two. e.IncRef() return &e, nil } -// Send implements transport.ConnectedEndpoint.Send. -func (c *ConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMessages transport.ControlMessages, from tcpip.FullAddress) (int64, bool, *syserr.Error) { +// SockType returns the underlying socket type. +func (c *HostConnectedEndpoint) SockType() linux.SockType { + return c.stype +} + +// Send implements ConnectedEndpoint.Send. +func (c *HostConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMessages ControlMessages, from tcpip.FullAddress) (int64, bool, *syserr.Error) { c.mu.RLock() defer c.mu.RUnlock() @@ -172,11 +179,11 @@ func (c *ConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMess return n, false, syserr.FromError(err) } -// SendNotify implements transport.ConnectedEndpoint.SendNotify. -func (c *ConnectedEndpoint) SendNotify() {} +// SendNotify implements ConnectedEndpoint.SendNotify. +func (c *HostConnectedEndpoint) SendNotify() {} -// CloseSend implements transport.ConnectedEndpoint.CloseSend. -func (c *ConnectedEndpoint) CloseSend() { +// CloseSend implements ConnectedEndpoint.CloseSend. +func (c *HostConnectedEndpoint) CloseSend() { c.mu.Lock() defer c.mu.Unlock() @@ -187,30 +194,30 @@ func (c *ConnectedEndpoint) CloseSend() { } } -// CloseNotify implements transport.ConnectedEndpoint.CloseNotify. -func (c *ConnectedEndpoint) CloseNotify() {} +// CloseNotify implements ConnectedEndpoint.CloseNotify. +func (c *HostConnectedEndpoint) CloseNotify() {} -// Writable implements transport.ConnectedEndpoint.Writable. -func (c *ConnectedEndpoint) Writable() bool { +// Writable implements ConnectedEndpoint.Writable. +func (c *HostConnectedEndpoint) Writable() bool { c.mu.RLock() defer c.mu.RUnlock() return fdnotifier.NonBlockingPoll(int32(c.fd), waiter.WritableEvents)&waiter.WritableEvents != 0 } -// Passcred implements transport.ConnectedEndpoint.Passcred. -func (c *ConnectedEndpoint) Passcred() bool { +// Passcred implements ConnectedEndpoint.Passcred. +func (c *HostConnectedEndpoint) Passcred() bool { // We don't support credential passing for host sockets. return false } -// GetLocalAddress implements transport.ConnectedEndpoint.GetLocalAddress. -func (c *ConnectedEndpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) { +// GetLocalAddress implements ConnectedEndpoint.GetLocalAddress. +func (c *HostConnectedEndpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) { return tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, nil } -// EventUpdate implements transport.ConnectedEndpoint.EventUpdate. -func (c *ConnectedEndpoint) EventUpdate() { +// EventUpdate implements ConnectedEndpoint.EventUpdate. +func (c *HostConnectedEndpoint) EventUpdate() { c.mu.RLock() defer c.mu.RUnlock() if c.fd != -1 { @@ -218,8 +225,8 @@ func (c *ConnectedEndpoint) EventUpdate() { } } -// Recv implements transport.Receiver.Recv. -func (c *ConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool, numRights int, peek bool) (int64, int64, transport.ControlMessages, bool, tcpip.FullAddress, bool, *syserr.Error) { +// Recv implements Receiver.Recv. +func (c *HostConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool, numRights int, peek bool) (int64, int64, ControlMessages, bool, tcpip.FullAddress, bool, *syserr.Error) { c.mu.RLock() defer c.mu.RUnlock() @@ -238,7 +245,7 @@ func (c *ConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool, err = nil } if err != nil { - return 0, 0, transport.ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err) + return 0, 0, ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err) } // There is no need for the callee to call RecvNotify because fdReadVec uses @@ -251,25 +258,25 @@ func (c *ConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool, // Avoid extra allocations in the case where there isn't any control data. if len(cm) == 0 { - return rl, ml, transport.ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil + return rl, ml, ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil } fds, err := cm.ExtractFDs() if err != nil { - return 0, 0, transport.ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err) + return 0, 0, ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err) } if len(fds) == 0 { - return rl, ml, transport.ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil + return rl, ml, ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil } - return rl, ml, control.NewVFS2(nil, nil, newSCMRights(fds)), cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil + return rl, ml, ControlMessages{Rights: &SCMRights{fds}}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil } -// RecvNotify implements transport.Receiver.RecvNotify. -func (c *ConnectedEndpoint) RecvNotify() {} +// RecvNotify implements Receiver.RecvNotify. +func (c *HostConnectedEndpoint) RecvNotify() {} -// CloseRecv implements transport.Receiver.CloseRecv. -func (c *ConnectedEndpoint) CloseRecv() { +// CloseRecv implements Receiver.CloseRecv. +func (c *HostConnectedEndpoint) CloseRecv() { c.mu.Lock() defer c.mu.Unlock() @@ -280,47 +287,46 @@ func (c *ConnectedEndpoint) CloseRecv() { } } -// Readable implements transport.Receiver.Readable. -func (c *ConnectedEndpoint) Readable() bool { +// Readable implements Receiver.Readable. +func (c *HostConnectedEndpoint) Readable() bool { c.mu.RLock() defer c.mu.RUnlock() return fdnotifier.NonBlockingPoll(int32(c.fd), waiter.ReadableEvents)&waiter.ReadableEvents != 0 } -// SendQueuedSize implements transport.Receiver.SendQueuedSize. -func (c *ConnectedEndpoint) SendQueuedSize() int64 { +// SendQueuedSize implements Receiver.SendQueuedSize. +func (c *HostConnectedEndpoint) SendQueuedSize() int64 { // TODO(gvisor.dev/issue/273): SendQueuedSize isn't supported for host // sockets because we don't allow the sentry to call ioctl(2). return -1 } -// RecvQueuedSize implements transport.Receiver.RecvQueuedSize. -func (c *ConnectedEndpoint) RecvQueuedSize() int64 { +// RecvQueuedSize implements Receiver.RecvQueuedSize. +func (c *HostConnectedEndpoint) RecvQueuedSize() int64 { // TODO(gvisor.dev/issue/273): RecvQueuedSize isn't supported for host // sockets because we don't allow the sentry to call ioctl(2). return -1 } -// SendMaxQueueSize implements transport.Receiver.SendMaxQueueSize. -func (c *ConnectedEndpoint) SendMaxQueueSize() int64 { +// SendMaxQueueSize implements Receiver.SendMaxQueueSize. +func (c *HostConnectedEndpoint) SendMaxQueueSize() int64 { return atomic.LoadInt64(&c.sndbuf) } -// RecvMaxQueueSize implements transport.Receiver.RecvMaxQueueSize. -func (c *ConnectedEndpoint) RecvMaxQueueSize() int64 { +// RecvMaxQueueSize implements Receiver.RecvMaxQueueSize. +func (c *HostConnectedEndpoint) RecvMaxQueueSize() int64 { // N.B. Unix sockets don't use the receive buffer. We'll claim it is // the same size as the send buffer. return atomic.LoadInt64(&c.sndbuf) } -func (c *ConnectedEndpoint) destroyLocked() { +func (c *HostConnectedEndpoint) destroyLocked() { c.fd = -1 } -// Release implements transport.ConnectedEndpoint.Release and -// transport.Receiver.Release. -func (c *ConnectedEndpoint) Release(ctx context.Context) { +// Release implements ConnectedEndpoint.Release and Receiver.Release. +func (c *HostConnectedEndpoint) Release(ctx context.Context) { c.DecRef(func() { c.mu.Lock() c.destroyLocked() @@ -328,18 +334,18 @@ func (c *ConnectedEndpoint) Release(ctx context.Context) { }) } -// CloseUnread implements transport.ConnectedEndpoint.CloseUnread. -func (c *ConnectedEndpoint) CloseUnread() {} +// CloseUnread implements ConnectedEndpoint.CloseUnread. +func (c *HostConnectedEndpoint) CloseUnread() {} -// SetSendBufferSize implements transport.ConnectedEndpoint.SetSendBufferSize. -func (c *ConnectedEndpoint) SetSendBufferSize(v int64) (newSz int64) { +// SetSendBufferSize implements ConnectedEndpoint.SetSendBufferSize. +func (c *HostConnectedEndpoint) SetSendBufferSize(v int64) (newSz int64) { // gVisor does not permit setting of SO_SNDBUF for host backed unix // domain sockets. return atomic.LoadInt64(&c.sndbuf) } -// SetReceiveBufferSize implements transport.ConnectedEndpoint.SetReceiveBufferSize. -func (c *ConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) { +// SetReceiveBufferSize implements ConnectedEndpoint.SetReceiveBufferSize. +func (c *HostConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) { // gVisor does not permit setting of SO_RCVBUF for host backed unix // domain sockets. Receive buffer does not have any effect for unix // sockets and we claim to be the same as send buffer. @@ -347,13 +353,13 @@ func (c *ConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) { } // SCMConnectedEndpoint represents an endpoint backed by a host fd that was -// passed through a gofer Unix socket. It resembles ConnectedEndpoint, with the +// passed through a gofer Unix socket. It resembles HostConnectedEndpoint, with the // following differences: // - SCMConnectedEndpoint is not saveable, because the host cannot guarantee // the same descriptor number across S/R. // - SCMConnectedEndpoint holds ownership of its fd and notification queue. type SCMConnectedEndpoint struct { - ConnectedEndpoint + HostConnectedEndpoint queue *waiter.Queue } @@ -363,8 +369,7 @@ func (e *SCMConnectedEndpoint) Init() error { return fdnotifier.AddFD(int32(e.fd), e.queue) } -// Release implements transport.ConnectedEndpoint.Release and -// transport.Receiver.Release. +// Release implements ConnectedEndpoint.Release and Receiver.Release. func (e *SCMConnectedEndpoint) Release(ctx context.Context) { e.DecRef(func() { e.mu.Lock() @@ -381,11 +386,11 @@ func (e *SCMConnectedEndpoint) Release(ctx context.Context) { // was passed through a Unix socket. // // The caller is responsible for calling Init(). Additionaly, Release needs to -// be called twice because ConnectedEndpoint is both a transport.Receiver and -// transport.ConnectedEndpoint. -func NewSCMEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue, addr string) (*SCMConnectedEndpoint, *syserr.Error) { +// be called twice because ConnectedEndpoint is both a Receiver and +// ConnectedEndpoint. +func NewSCMEndpoint(hostFD int, queue *waiter.Queue, addr string) (*SCMConnectedEndpoint, *syserr.Error) { e := SCMConnectedEndpoint{ - ConnectedEndpoint: ConnectedEndpoint{ + HostConnectedEndpoint: HostConnectedEndpoint{ fd: hostFD, addr: addr, }, diff --git a/pkg/sentry/fsimpl/host/socket_iovec.go b/pkg/sentry/socket/unix/transport/host_iovec.go similarity index 99% rename from pkg/sentry/fsimpl/host/socket_iovec.go rename to pkg/sentry/socket/unix/transport/host_iovec.go index 292b44c43..65aa35b1c 100644 --- a/pkg/sentry/fsimpl/host/socket_iovec.go +++ b/pkg/sentry/socket/unix/transport/host_iovec.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package host +package transport import ( "golang.org/x/sys/unix" diff --git a/pkg/sentry/fsimpl/host/socket_unsafe.go b/pkg/sentry/socket/unix/transport/host_unsafe.go similarity index 99% rename from pkg/sentry/fsimpl/host/socket_unsafe.go rename to pkg/sentry/socket/unix/transport/host_unsafe.go index 34f24e95c..2ac3835c1 100644 --- a/pkg/sentry/fsimpl/host/socket_unsafe.go +++ b/pkg/sentry/socket/unix/transport/host_unsafe.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package host +package transport import ( "unsafe" diff --git a/pkg/sentry/socket/unix/transport/save_restore.go b/pkg/sentry/socket/unix/transport/save_restore.go new file mode 100644 index 000000000..930b00f50 --- /dev/null +++ b/pkg/sentry/socket/unix/transport/save_restore.go @@ -0,0 +1,24 @@ +// Copyright 2021 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 transport + +import "fmt" + +// afterLoad is invoked by stateify. +func (c *HostConnectedEndpoint) afterLoad() { + if err := c.initFromOptions(); err != nil { + panic(fmt.Sprintf("initFromOptions failed: %v", err)) + } +} diff --git a/pkg/sentry/syscalls/linux/vfs2/BUILD b/pkg/sentry/syscalls/linux/vfs2/BUILD index 4a03008f8..3cb4727c9 100644 --- a/pkg/sentry/syscalls/linux/vfs2/BUILD +++ b/pkg/sentry/syscalls/linux/vfs2/BUILD @@ -53,6 +53,7 @@ go_library( "//pkg/sentry/fs/lock", "//pkg/sentry/fsbridge", "//pkg/sentry/fsimpl/eventfd", + "//pkg/sentry/fsimpl/host", "//pkg/sentry/fsimpl/pipefs", "//pkg/sentry/fsimpl/signalfd", "//pkg/sentry/fsimpl/timerfd", @@ -77,5 +78,6 @@ go_library( "//pkg/syserr", "//pkg/usermem", "//pkg/waiter", + "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/pkg/sentry/syscalls/linux/vfs2/socket.go b/pkg/sentry/syscalls/linux/vfs2/socket.go index d9ff33423..3eae2bd10 100644 --- a/pkg/sentry/syscalls/linux/vfs2/socket.go +++ b/pkg/sentry/syscalls/linux/vfs2/socket.go @@ -15,15 +15,20 @@ package vfs2 import ( + "fmt" "time" + "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/marshal" "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" "gvisor.dev/gvisor/pkg/sentry/socket" "gvisor.dev/gvisor/pkg/sentry/socket/control" @@ -744,6 +749,48 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc return uintptr(count), nil, nil } +func getSCMRightsVFS2(t *kernel.Task, rights transport.RightsControlMessage) control.SCMRightsVFS2 { + switch v := rights.(type) { + case control.SCMRightsVFS2: + return v + case *transport.SCMRights: + rf := control.RightsFilesVFS2(fdsToHostFiles(t, v.FDs)) + return &rf + default: + panic(fmt.Sprintf("rights of type %T must be *transport.SCMRights or implement SCMRightsVFS2", rights)) + } +} + +// If an error is encountered, only files created before the error will be +// returned. This is what Linux does. +func fdsToHostFiles(ctx context.Context, fds []int) []*vfs.FileDescription { + files := make([]*vfs.FileDescription, 0, len(fds)) + for _, fd := range fds { + // Get flags. We do it here because they may be modified + // by subsequent functions. + fileFlags, _, errno := unix.Syscall(unix.SYS_FCNTL, uintptr(fd), unix.F_GETFL, 0) + if errno != 0 { + ctx.Warningf("Error retrieving host FD flags: %v", error(errno)) + break + } + + // Create the file backed by hostFD. + file, err := host.NewFD(ctx, kernel.KernelFromContext(ctx).HostMount(), fd, &host.NewFDOptions{}) + if err != nil { + ctx.Warningf("Error creating file from host FD: %v", err) + break + } + + if err := file.SetStatusFlags(ctx, auth.CredentialsFromContext(ctx), uint32(fileFlags&linux.O_NONBLOCK)); err != nil { + ctx.Warningf("Error setting flags on host FD file: %v", err) + break + } + + files = append(files, file) + } + return files +} + func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr hostarch.Addr, flags int32, haveDeadline bool, deadline ktime.Time) (uintptr, error) { // Capture the message header and io vectors. var msg MessageHeader64 @@ -800,6 +847,7 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr hostarch.Addr, fl } if cms.Unix.Rights != nil { + cms.Unix.Rights = getSCMRightsVFS2(t, cms.Unix.Rights) controlData, mflags = control.PackRightsVFS2(t, cms.Unix.Rights.(control.SCMRightsVFS2), flags&linux.MSG_CMSG_CLOEXEC != 0, controlData, mflags) } diff --git a/pkg/sentry/uniqueid/BUILD b/pkg/sentry/uniqueid/BUILD index 7467e6398..04e2021d5 100644 --- a/pkg/sentry/uniqueid/BUILD +++ b/pkg/sentry/uniqueid/BUILD @@ -6,8 +6,5 @@ go_library( name = "uniqueid", srcs = ["context.go"], visibility = ["//pkg/sentry:internal"], - deps = [ - "//pkg/context", - "//pkg/sentry/socket/unix/transport", - ], + deps = ["//pkg/context"], ) diff --git a/pkg/sentry/uniqueid/context.go b/pkg/sentry/uniqueid/context.go index 1fb884a90..8dbdac400 100644 --- a/pkg/sentry/uniqueid/context.go +++ b/pkg/sentry/uniqueid/context.go @@ -18,7 +18,6 @@ package uniqueid import ( "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/sentry/socket/unix/transport" ) // contextID is the kernel package's type for context.Context.Value keys. @@ -38,14 +37,21 @@ const ( CtxInotifyCookie ) +// Provider generates a sequence of unique identifiers useful for, +// among other things, lock ordering. +type Provider interface { + // UniqueID returns a new unique identifier. + UniqueID() uint64 +} + // GlobalFromContext returns a system-wide unique identifier from ctx. func GlobalFromContext(ctx context.Context) uint64 { return ctx.Value(CtxGlobalUniqueID).(uint64) } // GlobalProviderFromContext returns a system-wide unique identifier from ctx. -func GlobalProviderFromContext(ctx context.Context) transport.UniqueIDProvider { - return ctx.Value(CtxGlobalUniqueIDProvider).(transport.UniqueIDProvider) +func GlobalProviderFromContext(ctx context.Context) Provider { + return ctx.Value(CtxGlobalUniqueIDProvider).(Provider) } // InotifyCookie generates a unique inotify event cookie from ctx.