Files
Tamir DubersteinandHanzo Dev 5d4b282975 checklocks: annotate network lock ownership
Make existing mutex and atomic ownership explicit for networking state.
Annotate self-locking helpers and synchronous forwarding paths, keeping
queue references and callback lifetime requirements in their comments.

Document collection, callback and helper identity limits where the
checker cannot enforce the existing ownership contract.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-26 12:56:52 -04:00

975 lines
30 KiB
Go

// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hostinet
import (
"fmt"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
"gvisor.dev/gvisor/pkg/sentry/hostfd"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/ktime"
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/control"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserr"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
const (
// sizeofSockaddr is the size in bytes of the largest sockaddr type
// supported by this package.
sizeofSockaddr = unix.SizeofSockaddrInet6 // sizeof(sockaddr_in6) > sizeof(sockaddr_in)
// maxControlLen is the maximum size of a control message buffer used in a
// recvmsg or sendmsg unix.
maxControlLen = 1024
)
// AllowedSocketType is a tuple of socket family, type, and protocol.
type AllowedSocketType struct {
Family int
Type int
// Protocol of AllowAllProtocols indicates that all protocols are
// allowed.
Protocol int
}
// AllowAllProtocols indicates that all protocols are allowed by the stack and
// in the syscall filters.
var AllowAllProtocols = -1
// AllowedSocketTypes are the socket types which are supported by hostinet.
// These are used to validate the arguments to socket(), and also to generate
// syscall filters.
var AllowedSocketTypes = []AllowedSocketType{
// Family, Type, Protocol.
{unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_TCP},
{unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_UDP},
{unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_ICMP},
{unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP},
{unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP},
{unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_ICMPV6},
}
// AllowedRawSocketTypes are the socket types which are supported by hostinet
// with raw sockets enabled.
var AllowedRawSocketTypes = []AllowedSocketType{
{unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_RAW},
{unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_TCP},
{unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_UDP},
{unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_ICMP},
{unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_RAW},
{unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_TCP},
{unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_UDP},
{unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_ICMPV6},
// AF_PACKET do not allow Write or SendMsg.
{unix.AF_PACKET, unix.SOCK_DGRAM, AllowAllProtocols},
{unix.AF_PACKET, unix.SOCK_RAW, AllowAllProtocols},
}
// Socket implements socket.Socket (and by extension, vfs.FileDescriptionImpl)
// for host sockets.
//
// +stateify savable
type Socket struct {
vfsfd vfs.FileDescription
vfs.FileDescriptionDefaultImpl
vfs.LockFD
// We store metadata for hostinet sockets internally. Technically, we should
// access metadata (e.g. through stat, chmod) on the host for correctness,
// but this is not very useful for inet socket fds, which do not belong to a
// concrete file anyway.
vfs.DentryMetadataFileDescriptionImpl
socket.SendReceiveTimeout
family int // Read-only.
stype linux.SockType // Read-only.
protocol int // Read-only.
queue waiter.Queue
// If persistentEventMask is non-zero, persistentEntry is a no-op waiter
// that is registered in queue for all events in persistentEventMask.
// Mutations to persistentEventMask and persistentEntry are serialized by
// persistentEventMu.
//
// Bits in persistentEventMask are set by EventRegister(), and never unset.
// This ensures that queue.Events() does not change (necessitating calls to
// fdnotifier.UpdateFD() => epoll_ctl(EPOLL_CTL_MOD)) when EventRegister()
// and EventUnregister() are frequently called with entries with the same
// masks, as when e.g. applications repeatedly call poll() with the same
// event mask, or blocking accept() / read() / write() / recvmsg() /
// sendmsg() / etc., on the same socket.
persistentEventMu sync.Mutex `state:"nosave"`
// +checklocks:persistentEventMu
// +checkatomic
persistentEventMask atomicbitops.Uint64
// +checklocks:persistentEventMu
persistentEntry waiter.Entry
// fd is the host socket fd. It must have O_NONBLOCK, so that operations
// will return EWOULDBLOCK instead of blocking on the host. This allows us to
// handle blocking behavior independently in the sentry.
fd int `state:"nosave"`
// recvClosed indicates that the socket has been shutdown for reading
// (SHUT_RD or SHUT_RDWR).
//
// +checkatomic
recvClosed atomicbitops.Bool
// listenBacklog is the backlog passed to the most recent listen(2).
//
// +checkatomic
listenBacklog atomicbitops.Int32
// savedListener is set by beforeSave if this socket is listening.
savedListener *listenerState
}
var _ = socket.Socket(&Socket{})
func newSocket(t *kernel.Task, family int, stype linux.SockType, protocol int, fd int, flags uint32) (*vfs.FileDescription, *syserr.Error) {
mnt := t.Kernel().SocketMount()
d := sockfs.NewDentry(t, mnt)
defer d.DecRef(t)
s := &Socket{
family: family,
stype: stype,
protocol: protocol,
fd: fd,
}
s.LockFD.Init(&vfs.FileLocks{})
if err := fdnotifier.AddFD(int32(fd), &s.queue); err != nil {
return nil, syserr.FromError(err)
}
vfsfd := &s.vfsfd
if err := vfsfd.Init(s, linux.O_RDWR|(flags&linux.O_NONBLOCK), t.Credentials(), mnt, d, &vfs.FileDescriptionOptions{
DenyPRead: true,
DenyPWrite: true,
UseDentryMetadata: true,
SpecialFile: true,
}); err != nil {
fdnotifier.RemoveFD(int32(s.fd))
return nil, syserr.FromError(err)
}
return vfsfd, nil
}
// Release implements vfs.FileDescriptionImpl.Release.
func (s *Socket) Release(ctx context.Context) {
kernel.KernelFromContext(ctx).DeleteSocket(&s.vfsfd)
if s.fd < 0 {
return
}
fdnotifier.RemoveFD(int32(s.fd))
_ = unix.Close(s.fd)
}
// Epollable implements FileDescriptionImpl.Epollable.
func (s *Socket) Epollable() bool {
return true
}
// Ioctl implements vfs.FileDescriptionImpl.
func (s *Socket) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
if s.fd < 0 {
return 0, linuxerr.ECONNRESET
}
return ioctl(ctx, s.fd, uio, sysno, args)
}
// PRead implements vfs.FileDescriptionImpl.PRead.
func (s *Socket) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
return 0, linuxerr.ESPIPE
}
// Read implements vfs.FileDescriptionImpl.
func (s *Socket) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
// All flags other than RWF_NOWAIT should be ignored.
// TODO(gvisor.dev/issue/2601): Support RWF_NOWAIT.
if opts.Flags != 0 {
return 0, linuxerr.EOPNOTSUPP
}
if s.fd < 0 {
return 0, linuxerr.ECONNRESET
}
reader := hostfd.GetReadWriterAt(int32(s.fd), -1, opts.Flags)
defer hostfd.PutReadWriterAt(reader)
n, err := dst.CopyOutFrom(ctx, reader)
return int64(n), err
}
// PWrite implements vfs.FileDescriptionImpl.
func (s *Socket) PWrite(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
return 0, linuxerr.ESPIPE
}
// Write implements vfs.FileDescriptionImpl.
func (s *Socket) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
if s.family == linux.AF_PACKET {
// Don't allow Write for AF_PACKET.
return 0, linuxerr.EACCES
}
// All flags other than RWF_NOWAIT should be ignored.
// TODO(gvisor.dev/issue/2601): Support RWF_NOWAIT.
if opts.Flags != 0 {
return 0, linuxerr.EOPNOTSUPP
}
if s.fd < 0 {
if s.stype == linux.SOCK_STREAM || s.stype == linux.SOCK_SEQPACKET {
if sendSig := linux.SignalNoInfoFuncFromContext(ctx); sendSig != nil {
sendSig(linux.SIGPIPE)
}
return 0, linuxerr.EPIPE
}
return 0, linuxerr.ECONNRESET
}
writer := hostfd.GetReadWriterAt(int32(s.fd), -1, opts.Flags)
defer hostfd.PutReadWriterAt(writer)
n, err := src.CopyInTo(ctx, writer)
return int64(n), err
}
type socketProvider struct {
family int
}
// Socket implements socket.Provider.Socket.
func (p *socketProvider) Socket(t *kernel.Task, stypeflags linux.SockType, protocol int) (*vfs.FileDescription, *syserr.Error) {
// Check that we are using the host network stack.
netCtx := t.NetworkContext()
if netCtx == nil {
return nil, nil
}
stack, ok := netCtx.(*Stack)
if !ok {
return nil, nil
}
stype := stypeflags & linux.SOCK_TYPE_MASK
// Raw and packet sockets require CAP_NET_RAW.
if stype == linux.SOCK_RAW || p.family == linux.AF_PACKET {
if !t.HasCapabilityIn(linux.CAP_NET_RAW, t.NetworkNamespace().UserNamespace()) {
return nil, syserr.ErrNotPermitted
}
}
// Convert generic IPPROTO_IP protocol to the actual protocol depending
// on family and type.
if protocol == linux.IPPROTO_IP && (p.family == linux.AF_INET || p.family == linux.AF_INET6) {
switch stype {
case linux.SOCK_STREAM:
protocol = linux.IPPROTO_TCP
case linux.SOCK_DGRAM:
protocol = linux.IPPROTO_UDP
}
}
// Validate the socket based on family, type, and protocol.
var supported bool
for _, allowed := range stack.allowedSocketTypes {
isAllowedFamily := p.family == allowed.Family
isAllowedType := int(stype) == allowed.Type
isAllowedProtocol := protocol == allowed.Protocol || allowed.Protocol == AllowAllProtocols
if isAllowedFamily && isAllowedType && isAllowedProtocol {
supported = true
break
}
}
if !supported {
// Return nil error here to give other socket providers a
// chance to create this socket.
return nil, nil
}
// Conservatively ignore all flags specified by the application and add
// SOCK_NONBLOCK since socketOperations requires it.
st := int(stype) | unix.SOCK_NONBLOCK | unix.SOCK_CLOEXEC
fd, err := unix.Socket(p.family, st, protocol)
if err != nil {
return nil, syserr.FromError(err)
}
return newSocket(t, p.family, stype, protocol, fd, uint32(stypeflags&unix.SOCK_NONBLOCK))
}
// Pair implements socket.Provider.Pair.
func (p *socketProvider) Pair(t *kernel.Task, stype linux.SockType, protocol int) (*vfs.FileDescription, *vfs.FileDescription, *syserr.Error) {
// Not supported by AF_INET/AF_INET6.
return nil, nil, nil
}
// Readiness implements waiter.Waitable.Readiness.
func (s *Socket) Readiness(mask waiter.EventMask) waiter.EventMask {
if s.fd < 0 {
return waiter.EventHUp | waiter.EventErr | waiter.EventRdHUp
}
return fdnotifier.NonBlockingPoll(int32(s.fd), mask)
}
// EventRegister implements waiter.Waitable.EventRegister.
//
// +checklocksexclude:s.persistentEventMu
func (s *Socket) EventRegister(e *waiter.Entry) error {
if s.fd < 0 {
s.queue.EventRegister(e)
return nil
}
if em, pem := e.Mask(), waiter.EventMask(s.persistentEventMask.Load()); em&^pem != 0 {
s.persistentEventMu.Lock()
pem = waiter.EventMask(s.persistentEventMask.RacyLoad())
if newMask := pem | em; pem != newMask {
if pem == 0 {
s.persistentEntry.Init(waiter.NoopListener{}, newMask)
s.queue.EventRegister(&s.persistentEntry)
} else {
s.persistentEntry.SetQueuedMask(&s.queue, newMask)
}
if err := fdnotifier.UpdateFD(int32(s.fd)); err != nil {
if pem == 0 {
s.queue.EventUnregister(&s.persistentEntry)
}
s.persistentEventMu.Unlock()
return err
}
s.persistentEventMask.Store(uint64(newMask))
}
s.persistentEventMu.Unlock()
}
s.queue.EventRegister(e)
return nil
}
// EventUnregister implements waiter.Waitable.EventUnregister.
func (s *Socket) EventUnregister(e *waiter.Entry) {
s.queue.EventUnregister(e)
}
// eventRegisterTransient is equivalent to EventRegister, but does not keep
// events registered with fdnotifier after eventUnregisterTransient. This is
// used when there is no reason to expect the same events to be registered
// again in the future.
func (s *Socket) eventRegisterTransient(e *waiter.Entry) error {
s.queue.EventRegister(e)
if err := fdnotifier.UpdateFD(int32(s.fd)); err != nil {
s.queue.EventUnregister(e)
return err
}
return nil
}
// eventUnregisterTransient must be used instead of EventUnregister to
// unregister notifiers registered by eventRegisterTransient.
func (s *Socket) eventUnregisterTransient(e *waiter.Entry) {
s.queue.EventUnregister(e)
if err := fdnotifier.UpdateFD(int32(s.fd)); err != nil {
panic(err)
}
}
// Connect implements socket.Socket.Connect.
func (s *Socket) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error {
if s.fd < 0 {
return syserr.ErrConnectionAborted
}
if len(sockaddr) > sizeofSockaddr {
sockaddr = sockaddr[:sizeofSockaddr]
}
_, _, errno := unix.Syscall(unix.SYS_CONNECT, uintptr(s.fd), uintptr(firstBytePtr(sockaddr)), uintptr(len(sockaddr)))
if errno == 0 {
return nil
}
// The host socket is always non-blocking, so we expect connect to
// return EINPROGRESS. If we are emulating a blocking socket, we will
// wait for the connect to complete below.
// But if we are not emulating a blocking socket, or if we got some
// other error, then return it now.
if errno != unix.EINPROGRESS || !blocking {
return syserr.FromError(translateIOSyscallError(errno))
}
// "EINPROGRESS: The socket is nonblocking and the connection cannot be
// completed immediately. It is possible to select(2) or poll(2) for
// completion by selecting the socket for writing. After select(2)
// indicates writability, use getsockopt(2) to read the SO_ERROR option at
// level SOL-SOCKET to determine whether connect() completed successfully
// (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error
// codes listed here, explaining the reason for the failure)." - connect(2)
writableMask := waiter.WritableEvents
e, ch := waiter.NewChannelEntry(writableMask)
s.eventRegisterTransient(&e)
defer s.eventUnregisterTransient(&e)
if s.Readiness(writableMask)&writableMask == 0 {
if err := t.Block(ch); err != nil {
return syserr.FromError(err)
}
}
val, err := unix.GetsockoptInt(s.fd, unix.SOL_SOCKET, unix.SO_ERROR)
if err != nil {
return syserr.FromError(err)
}
if val != 0 {
return syserr.FromError(unix.Errno(uintptr(val)))
}
// It seems like we are all good now, but Linux has left the socket
// state as CONNECTING (not CONNECTED). This is a strange quirk of
// non-blocking sockets. See tcp_finish_connect() which sets tcp state
// but not socket state.
//
// Sockets in the CONNECTING state can call connect() a second time,
// whereas CONNECTED sockets will reject the second connect() call.
// Because we are emulating a blocking socket, we want a subsequent
// connect() call to fail. So we must kick Linux to update the socket
// to state CONNECTED, which we can do by calling connect() a second
// time ourselves.
_, _, errno = unix.Syscall(unix.SYS_CONNECT, uintptr(s.fd), uintptr(firstBytePtr(sockaddr)), uintptr(len(sockaddr)))
if errno != 0 && errno != unix.EALREADY {
return syserr.FromError(translateIOSyscallError(errno))
}
return nil
}
// Accept implements socket.Socket.Accept.
//
// +checklocksexclude:s.persistentEventMu
func (s *Socket) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (int32, linux.SockAddr, uint32, *syserr.Error) {
if s.fd < 0 {
return 0, nil, 0, syserr.ErrConnectionAborted
}
var peerAddr linux.SockAddr
var peerAddrBuf []byte
var peerAddrlen uint32
var peerAddrPtr *byte
var peerAddrlenPtr *uint32
if peerRequested {
peerAddrBuf = make([]byte, sizeofSockaddr)
peerAddrlen = uint32(len(peerAddrBuf))
peerAddrPtr = &peerAddrBuf[0]
peerAddrlenPtr = &peerAddrlen
}
// Conservatively ignore all flags specified by the application and add
// SOCK_NONBLOCK since socketOpsCommon requires it.
fd, syscallErr := accept4(s.fd, peerAddrPtr, peerAddrlenPtr, unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC)
if blocking {
var ch chan struct{}
for linuxerr.Equals(linuxerr.ErrWouldBlock, syscallErr) {
if ch != nil {
if syscallErr = t.Block(ch); syscallErr != nil {
break
}
} else {
var e waiter.Entry
e, ch = waiter.NewChannelEntry(waiter.ReadableEvents | waiter.EventHUp | waiter.EventErr)
s.EventRegister(&e)
defer s.EventUnregister(&e)
}
fd, syscallErr = accept4(s.fd, peerAddrPtr, peerAddrlenPtr, unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC)
}
}
if peerRequested {
peerAddr = socket.UnmarshalSockAddr(s.family, peerAddrBuf[:peerAddrlen])
}
if syscallErr != nil {
return 0, peerAddr, peerAddrlen, syserr.FromError(syscallErr)
}
var (
kfd int32
kerr error
)
f, err := newSocket(t, s.family, s.stype, s.protocol, fd, uint32(flags&unix.SOCK_NONBLOCK))
if err != nil {
_ = unix.Close(fd)
return 0, nil, 0, err
}
defer f.DecRef(t)
kfd, kerr = t.NewFDFrom(0, f, kernel.FDFlags{
CloseOnExec: flags&unix.SOCK_CLOEXEC != 0,
})
t.Kernel().RecordSocket(f)
return kfd, peerAddr, peerAddrlen, syserr.FromError(kerr)
}
// Bind implements socket.Socket.Bind.
func (s *Socket) Bind(_ *kernel.Task, sockaddr []byte) *syserr.Error {
if s.fd < 0 {
return syserr.ErrConnectionAborted
}
if len(sockaddr) > sizeofSockaddr {
sockaddr = sockaddr[:sizeofSockaddr]
}
if err := bind(s.fd, sockaddr); err != nil {
return syserr.FromError(err)
}
return nil
}
// Listen implements socket.Socket.Listen.
func (s *Socket) Listen(_ *kernel.Task, backlog int) *syserr.Error {
if s.fd < 0 {
return syserr.ErrConnectionAborted
}
if err := unix.Listen(s.fd, backlog); err != nil {
return syserr.FromError(err)
}
s.listenBacklog.Store(int32(backlog))
return nil
}
// Shutdown implements socket.Socket.Shutdown.
func (s *Socket) Shutdown(_ *kernel.Task, how int) *syserr.Error {
if s.fd < 0 {
return syserr.ErrConnectionAborted
}
switch how {
case unix.SHUT_RD, unix.SHUT_RDWR:
// Mark the socket as closed for reading.
s.recvClosed.Store(true)
fallthrough
case unix.SHUT_WR:
return syserr.FromError(unix.Shutdown(s.fd, how))
default:
return syserr.ErrInvalidArgument
}
}
func (s *Socket) recvMsgFromHost(iovs []unix.Iovec, flags int, senderRequested bool, controlLen uint64) (uint64, int, []byte, []byte, error) {
// We always do a non-blocking recv*().
sysflags := flags | unix.MSG_DONTWAIT
msg := unix.Msghdr{}
if len(iovs) > 0 {
msg.Iov = &iovs[0]
msg.Iovlen = uint64(len(iovs))
}
var senderAddrBuf []byte
if senderRequested {
senderAddrBuf = make([]byte, sizeofSockaddr)
msg.Name = &senderAddrBuf[0]
msg.Namelen = uint32(sizeofSockaddr)
}
var controlBuf []byte
if controlLen > 0 {
if controlLen > maxControlLen {
controlLen = maxControlLen
}
controlBuf = make([]byte, controlLen)
msg.Control = &controlBuf[0]
msg.Controllen = controlLen
}
n, err := recvmsg(s.fd, &msg, sysflags)
if err != nil {
return 0 /* n */, 0 /* mFlags */, nil /* senderAddrBuf */, nil /* controlBuf */, err
}
return n, int(msg.Flags), senderAddrBuf[:msg.Namelen], controlBuf[:msg.Controllen], err
}
const allowedRecvMsgFlags = unix.MSG_CTRUNC |
unix.MSG_DONTWAIT |
unix.MSG_ERRQUEUE |
unix.MSG_OOB |
unix.MSG_PEEK |
unix.MSG_TRUNC |
unix.MSG_WAITALL
// RecvMsg implements socket.Socket.RecvMsg.
//
// +checklocksexclude:s.persistentEventMu
func (s *Socket) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlLen uint64) (int, int, linux.SockAddr, uint32, socket.ControlMessages, *syserr.Error) {
// Only allow known and safe flags.
if flags&^allowedRecvMsgFlags != 0 {
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrInvalidArgument
}
if s.fd < 0 {
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrConnectionReset
}
var senderAddrBuf []byte
var controlBuf []byte
var msgFlags int
copyToDst := func() (int64, error) {
var n uint64
var err error
if dst.NumBytes() == 0 {
// We want to make the recvmsg(2) call to the host even if dst is empty
// to fetch control messages, sender address or errors if any occur.
n, msgFlags, senderAddrBuf, controlBuf, err = s.recvMsgFromHost(nil, flags, senderRequested, controlLen)
return int64(n), err
}
recvmsgToBlocks := safemem.ReaderFunc(func(dsts safemem.BlockSeq) (uint64, error) {
// Refuse to do anything if any part of dst.Addrs was unusable.
if uint64(dst.NumBytes()) != dsts.NumBytes() {
return 0, nil
}
if dsts.IsEmpty() {
return 0, nil
}
n, msgFlags, senderAddrBuf, controlBuf, err = s.recvMsgFromHost(safemem.IovecsFromBlockSeq(dsts), flags, senderRequested, controlLen)
return n, err
})
return dst.CopyOutFrom(t, recvmsgToBlocks)
}
var ch chan struct{}
n, err := copyToDst()
// recv*(MSG_ERRQUEUE) never blocks, even without MSG_DONTWAIT.
if flags&(unix.MSG_DONTWAIT|unix.MSG_ERRQUEUE) == 0 {
for linuxerr.Equals(linuxerr.ErrWouldBlock, err) {
// We only expect blocking to come from the actual syscall, in which
// case it can't have returned any data.
if n != 0 {
panic(fmt.Sprintf("CopyOutFrom: got (%d, %v), wanted (0, %v)", n, err, err))
}
// Are we closed for reading? No sense in trying to read if so.
if s.recvClosed.Load() {
break
}
if ch != nil {
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = linuxerr.ErrWouldBlock
}
break
}
} else {
var e waiter.Entry
e, ch = waiter.NewChannelEntry(waiter.ReadableEvents | waiter.EventRdHUp | waiter.EventHUp | waiter.EventErr)
s.EventRegister(&e)
defer s.EventUnregister(&e)
}
n, err = copyToDst()
}
}
if err != nil {
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
}
// In some circumstances (like MSG_PEEK specified), the sender address
// field is purposefully ignored. recvMsgFromHost will return an empty
// senderAddrBuf in those cases.
var senderAddr linux.SockAddr
if senderRequested && len(senderAddrBuf) > 0 {
senderAddr = socket.UnmarshalSockAddr(s.family, senderAddrBuf)
}
unixControlMessages, err := unix.ParseSocketControlMessage(controlBuf)
if err != nil {
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
}
return int(n), msgFlags, senderAddr, uint32(len(senderAddrBuf)), parseUnixControlMessages(unixControlMessages), nil
}
func parseUnixControlMessages(unixControlMessages []unix.SocketControlMessage) socket.ControlMessages {
controlMessages := socket.ControlMessages{}
for _, unixCmsg := range unixControlMessages {
switch unixCmsg.Header.Level {
case linux.SOL_SOCKET:
switch unixCmsg.Header.Type {
case linux.SO_TIMESTAMP:
controlMessages.IP.HasTimestamp = true
ts := linux.Timeval{}
ts.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.Timestamp = ts.ToTime()
}
case linux.SOL_IP:
switch unixCmsg.Header.Type {
case linux.IP_TOS:
controlMessages.IP.HasTOS = true
var tos primitive.Uint8
tos.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.TOS = uint8(tos)
case linux.IP_TTL:
controlMessages.IP.HasTTL = true
var ttl primitive.Uint32
ttl.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.TTL = uint32(ttl)
case linux.IP_PKTINFO:
controlMessages.IP.HasIPPacketInfo = true
var packetInfo linux.ControlMessageIPPacketInfo
packetInfo.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.PacketInfo = packetInfo
case linux.IP_RECVORIGDSTADDR:
var addr linux.SockAddrInet
addr.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.OriginalDstAddress = &addr
case unix.IP_RECVERR:
var errCmsg linux.SockErrCMsgIPv4
errCmsg.UnmarshalBytes(unixCmsg.Data)
controlMessages.IP.SockErr = &errCmsg
}
case linux.SOL_IPV6:
switch unixCmsg.Header.Type {
case linux.IPV6_TCLASS:
controlMessages.IP.HasTClass = true
var tclass primitive.Uint32
tclass.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.TClass = uint32(tclass)
case linux.IPV6_PKTINFO:
controlMessages.IP.HasIPv6PacketInfo = true
var packetInfo linux.ControlMessageIPv6PacketInfo
packetInfo.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.IPv6PacketInfo = packetInfo
case linux.IPV6_HOPLIMIT:
controlMessages.IP.HasHopLimit = true
var hoplimit primitive.Uint32
hoplimit.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.HopLimit = uint32(hoplimit)
case linux.IPV6_RECVORIGDSTADDR:
var addr linux.SockAddrInet6
addr.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.OriginalDstAddress = &addr
case unix.IPV6_RECVERR:
var errCmsg linux.SockErrCMsgIPv6
errCmsg.UnmarshalBytes(unixCmsg.Data)
controlMessages.IP.SockErr = &errCmsg
}
case linux.SOL_TCP:
switch unixCmsg.Header.Type {
case linux.TCP_INQ:
controlMessages.IP.HasInq = true
var inq primitive.Int32
inq.UnmarshalUnsafe(unixCmsg.Data)
controlMessages.IP.Inq = int32(inq)
}
}
}
return controlMessages
}
const allowedSendMsgFlags = unix.MSG_DONTWAIT |
unix.MSG_EOR |
unix.MSG_FASTOPEN |
unix.MSG_MORE |
unix.MSG_NOSIGNAL |
unix.MSG_OOB
// SendMsg implements socket.Socket.SendMsg.
//
// +checklocksexclude:s.persistentEventMu
func (s *Socket) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
if s.family == linux.AF_PACKET {
// Don't allow SendMesg for AF_PACKET.
return 0, syserr.ErrPermissionDenied
}
// Only allow known and safe flags.
if flags&^allowedSendMsgFlags != 0 {
return 0, syserr.ErrInvalidArgument
}
if s.fd < 0 {
if s.stype == linux.SOCK_STREAM || s.stype == linux.SOCK_SEQPACKET {
t.SendSignal(kernel.SignalInfoNoInfo(linux.SIGPIPE, t, t))
return 0, syserr.ErrBrokenPipe
}
return 0, syserr.ErrConnectionReset
}
// If the src is zero-length, call SENDTO directly with a null buffer in
// order to generate poll/epoll notifications.
if src.NumBytes() == 0 {
sysflags := flags | unix.MSG_DONTWAIT
n, _, errno := unix.Syscall6(unix.SYS_SENDTO, uintptr(s.fd), 0, 0, uintptr(sysflags), uintptr(firstBytePtr(to)), uintptr(len(to)))
if errno != 0 {
return 0, syserr.FromError(errno)
}
return int(n), nil
}
space := uint64(control.CmsgsSpace(t, controlMessages))
if space > maxControlLen {
space = maxControlLen
}
controlBuf := make([]byte, 0, space)
// PackControlMessages will append up to space bytes to controlBuf.
controlBuf = control.PackControlMessages(t, controlMessages, controlBuf)
sendmsgFromBlocks := safemem.WriterFunc(func(srcs safemem.BlockSeq) (uint64, error) {
// Refuse to do anything if any part of src.Addrs was unusable.
if uint64(src.NumBytes()) != srcs.NumBytes() {
return 0, nil
}
if srcs.IsEmpty() && len(controlBuf) == 0 {
return 0, nil
}
// We always do a non-blocking send*().
sysflags := flags | unix.MSG_DONTWAIT
if srcs.NumBlocks() == 1 && len(controlBuf) == 0 {
// Skip allocating []unix.Iovec.
src := srcs.Head()
n, _, errno := unix.Syscall6(unix.SYS_SENDTO, uintptr(s.fd), src.Addr(), uintptr(src.Len()), uintptr(sysflags), uintptr(firstBytePtr(to)), uintptr(len(to)))
if errno != 0 {
return 0, translateIOSyscallError(errno)
}
return uint64(n), nil
}
iovs := safemem.IovecsFromBlockSeq(srcs)
msg := unix.Msghdr{
Iov: &iovs[0],
Iovlen: uint64(len(iovs)),
}
if len(to) != 0 {
msg.Name = &to[0]
msg.Namelen = uint32(len(to))
}
if len(controlBuf) != 0 {
msg.Control = &controlBuf[0]
msg.Controllen = uint64(len(controlBuf))
}
return sendmsg(s.fd, &msg, sysflags)
})
var ch chan struct{}
n, err := src.CopyInTo(t, sendmsgFromBlocks)
if flags&unix.MSG_DONTWAIT == 0 {
for linuxerr.Equals(linuxerr.ErrWouldBlock, err) {
// We only expect blocking to come from the actual syscall, in which
// case it can't have returned any data.
if n != 0 {
panic(fmt.Sprintf("CopyInTo: got (%d, %v), wanted (0, %v)", n, err, err))
}
if ch != nil {
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = linuxerr.ErrWouldBlock
}
break
}
} else {
var e waiter.Entry
e, ch = waiter.NewChannelEntry(waiter.WritableEvents | waiter.EventHUp | waiter.EventErr)
s.EventRegister(&e)
defer s.EventUnregister(&e)
}
n, err = src.CopyInTo(t, sendmsgFromBlocks)
}
}
return int(n), syserr.FromError(err)
}
func translateIOSyscallError(err error) error {
if err == unix.EAGAIN || err == unix.EWOULDBLOCK {
return linuxerr.ErrWouldBlock
}
return err
}
// State implements socket.Socket.State.
func (s *Socket) State() uint32 {
if s.fd < 0 {
if s.stype == linux.SOCK_STREAM {
return linux.TCP_CLOSE
}
return 0
}
info := linux.TCPInfo{}
buf := make([]byte, linux.SizeOfTCPInfo)
var err error
buf, err = getsockopt(s.fd, unix.SOL_TCP, unix.TCP_INFO, buf)
if err != nil {
if err != unix.ENOPROTOOPT {
log.Warningf("Failed to get TCP socket info from %+v: %v", s, err)
}
// For non-TCP sockets, silently ignore the failure.
return 0
}
if len(buf) != linux.SizeOfTCPInfo {
// Unmarshal below will panic if getsockopt returns a buffer of
// unexpected size.
log.Warningf("Failed to get TCP socket info from %+v: getsockopt(2) returned %d bytes, expecting %d bytes.", s, len(buf), linux.SizeOfTCPInfo)
return 0
}
info.UnmarshalUnsafe(buf[:info.SizeBytes()])
return uint32(info.State)
}
// Type implements socket.Socket.Type.
func (s *Socket) Type() (family int, skType linux.SockType, protocol int) {
return s.family, s.stype, s.protocol
}
func init() {
// Register all families in AllowedSocketTypes and AllowedRawSocket
// types. If we don't allow raw sockets, they will be rejected in the
// Socket call.
registered := make(map[int]struct{})
for _, sockType := range append(AllowedSocketTypes, AllowedRawSocketTypes...) {
fam := sockType.Family
if _, ok := registered[fam]; ok {
continue
}
socket.RegisterProvider(fam, &socketProvider{fam})
registered[fam] = struct{}{}
}
}
// GetPeerCreds implements socket.Socket.GetPeerCreds
func (s *Socket) GetPeerCreds(t *kernel.Task) (marshal.Marshallable, *syserr.Error) {
return nil, syserr.ErrNotSupported
}