Implement SO_SNDTIMEO

PiperOrigin-RevId: 225620490
Change-Id: Ia726107b3f58093a5f881634f90b071b32d2c269
This commit is contained in:
Ian Gudger 2018-12-14 16:12:51 -08:00 committed by Shentubot
parent ed930354ef
commit e1dcf92ec5
12 changed files with 307 additions and 41 deletions

View File

@ -71,6 +71,7 @@ go_test(
"//pkg/sentry/context",
"//pkg/sentry/context/contexttest",
"//pkg/sentry/fs",
"//pkg/sentry/kernel/time",
"//pkg/sentry/socket",
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/usermem",

View File

@ -21,6 +21,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/fd"
"gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest"
ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
@ -142,7 +143,7 @@ func TestSocketSendMsgLen0(t *testing.T) {
defer sfile.DecRef()
s := sfile.FileOperations.(socket.Socket)
n, terr := s.SendMsg(nil, usermem.BytesIOSequence(nil), []byte{}, 0, socket.ControlMessages{})
n, terr := s.SendMsg(nil, usermem.BytesIOSequence(nil), []byte{}, 0, false, ktime.Time{}, socket.ControlMessages{})
if n != 0 {
t.Fatalf("socket sendmsg() failed: %v wrote: %d", terr, n)
}

View File

@ -30,6 +30,7 @@ import (
"strings"
"sync"
"syscall"
"time"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/binary"
@ -137,12 +138,12 @@ type commonEndpoint interface {
//
// +stateify savable
type SocketOperations struct {
socket.ReceiveTimeout
fsutil.PipeSeek `state:"nosave"`
fsutil.NotDirReaddir `state:"nosave"`
fsutil.NoFsync `state:"nosave"`
fsutil.NoopFlush `state:"nosave"`
fsutil.NoMMap `state:"nosave"`
socket.SendReceiveTimeout
*waiter.Queue
family int
@ -643,7 +644,16 @@ func getSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, family
}
return syscall.Linger{}, nil
case linux.SO_SNDTIMEO:
// TODO: Linux allows shorter lengths for partial results.
if outLen < linux.SizeOfTimeval {
return nil, syserr.ErrInvalidArgument
}
return linux.NsecToTimeval(s.SendTimeout()), nil
case linux.SO_RCVTIMEO:
// TODO: Linux allows shorter lengths for partial results.
if outLen < linux.SizeOfTimeval {
return nil, syserr.ErrInvalidArgument
}
@ -833,6 +843,19 @@ func setSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, name i
v := usermem.ByteOrder.Uint32(optVal)
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.PasscredOption(v)))
case linux.SO_SNDTIMEO:
if len(optVal) < linux.SizeOfTimeval {
return syserr.ErrInvalidArgument
}
var v linux.Timeval
binary.Unmarshal(optVal[:linux.SizeOfTimeval], usermem.ByteOrder, &v)
if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) {
return syserr.ErrDomain
}
s.SetSendTimeout(v.ToNsecCapped())
return nil
case linux.SO_RCVTIMEO:
if len(optVal) < linux.SizeOfTimeval {
return syserr.ErrInvalidArgument
@ -840,6 +863,9 @@ func setSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, name i
var v linux.Timeval
binary.Unmarshal(optVal[:linux.SizeOfTimeval], usermem.ByteOrder, &v)
if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) {
return syserr.ErrDomain
}
s.SetRecvTimeout(v.ToNsecCapped())
return nil
@ -1365,7 +1391,7 @@ func (s *SocketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
// SendMsg implements the linux syscall sendmsg(2) for sockets backed by
// tcpip.Endpoint.
func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, controlMessages socket.ControlMessages) (int, *syserr.Error) {
func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
// Reject Unix control messages.
if !controlMessages.Unix.Empty() {
return 0, syserr.ErrInvalidArgument
@ -1431,7 +1457,10 @@ func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
return int(total), nil
}
if err := t.Block(ch); err != nil {
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if err == syserror.ETIMEDOUT {
return int(total), syserr.ErrTryAgain
}
// handleIOError will consume errors from t.Block if needed.
return int(total), syserr.FromError(err)
}

View File

@ -46,12 +46,12 @@ const (
// socketOperations implements fs.FileOperations and socket.Socket for a socket
// implemented using a host socket.
type socketOperations struct {
socket.ReceiveTimeout
fsutil.PipeSeek `state:"nosave"`
fsutil.NotDirReaddir `state:"nosave"`
fsutil.NoFsync `state:"nosave"`
fsutil.NoopFlush `state:"nosave"`
fsutil.NoMMap `state:"nosave"`
socket.SendReceiveTimeout
fd int // must be O_NONBLOCK
queue waiter.Queue
@ -418,7 +418,7 @@ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
}
// SendMsg implements socket.Socket.SendMsg.
func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, controlMessages socket.ControlMessages) (int, *syserr.Error) {
func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
// Whitelist flags.
if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_EOR|syscall.MSG_FASTOPEN|syscall.MSG_MORE|syscall.MSG_NOSIGNAL) != 0 {
return 0, syserr.ErrInvalidArgument
@ -468,7 +468,10 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
panic(fmt.Sprintf("CopyInTo: got (%d, %v), wanted (0, %v)", n, err, err))
}
if ch != nil {
if err = t.Block(ch); err != nil {
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if err == syserror.ETIMEDOUT {
err = syserror.ErrWouldBlock
}
break
}
} else {

View File

@ -65,12 +65,12 @@ var netlinkSocketDevice = device.NewAnonDevice()
//
// +stateify savable
type Socket struct {
socket.ReceiveTimeout
fsutil.PipeSeek `state:"nosave"`
fsutil.NotDirReaddir `state:"nosave"`
fsutil.NoFsync `state:"nosave"`
fsutil.NoopFlush `state:"nosave"`
fsutil.NoMMap `state:"nosave"`
socket.SendReceiveTimeout
// ports provides netlink port allocation.
ports *port.Manager
@ -593,7 +593,7 @@ func (s *Socket) sendMsg(ctx context.Context, src usermem.IOSequence, to []byte,
}
// SendMsg implements socket.Socket.SendMsg.
func (s *Socket) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, controlMessages socket.ControlMessages) (int, *syserr.Error) {
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) {
return s.sendMsg(t, src, to, flags, controlMessages)
}

View File

@ -17,6 +17,7 @@ package rpcinet
import (
"sync/atomic"
"syscall"
"time"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/binary"
@ -44,12 +45,12 @@ import (
// socketOperations implements fs.FileOperations and socket.Socket for a socket
// implemented using a host socket.
type socketOperations struct {
socket.ReceiveTimeout
fsutil.PipeSeek `state:"nosave"`
fsutil.NotDirReaddir `state:"nosave"`
fsutil.NoFsync `state:"nosave"`
fsutil.NoopFlush `state:"nosave"`
fsutil.NoMMap `state:"nosave"`
socket.SendReceiveTimeout
fd uint32 // must be O_NONBLOCK
wq *waiter.Queue
@ -379,7 +380,8 @@ func (s *socketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error {
// GetSockOpt implements socket.Socket.GetSockOpt.
func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outLen int) (interface{}, *syserr.Error) {
// SO_RCVTIMEO is special because blocking is performed within the sentry.
// SO_RCVTIMEO and SO_SNDTIMEO are special because blocking is performed
// within the sentry.
if level == linux.SOL_SOCKET && name == linux.SO_RCVTIMEO {
if outLen < linux.SizeOfTimeval {
return nil, syserr.ErrInvalidArgument
@ -387,6 +389,13 @@ func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outLe
return linux.NsecToTimeval(s.RecvTimeout()), nil
}
if level == linux.SOL_SOCKET && name == linux.SO_SNDTIMEO {
if outLen < linux.SizeOfTimeval {
return nil, syserr.ErrInvalidArgument
}
return linux.NsecToTimeval(s.SendTimeout()), nil
}
stack := t.NetworkContext().(*Stack)
id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_GetSockOpt{&pb.GetSockOptRequest{Fd: s.fd, Level: int64(level), Name: int64(name), Length: uint32(outLen)}}}, false /* ignoreResult */)
@ -403,8 +412,9 @@ func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outLe
// SetSockOpt implements socket.Socket.SetSockOpt.
func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *syserr.Error {
// Because blocking actually happens within the sentry we need to inspect
// this socket option to determine if it's a SO_RCVTIMEO, and if so, we will
// save it and use it as the deadline for recv(2) related syscalls.
// this socket option to determine if it's a SO_RCVTIMEO or SO_SNDTIMEO,
// and if so, we will save it and use it as the deadline for recv(2)
// or send(2) related syscalls.
if level == linux.SOL_SOCKET && name == linux.SO_RCVTIMEO {
if len(opt) < linux.SizeOfTimeval {
return syserr.ErrInvalidArgument
@ -412,9 +422,25 @@ func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt [
var v linux.Timeval
binary.Unmarshal(opt[:linux.SizeOfTimeval], usermem.ByteOrder, &v)
if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) {
return syserr.ErrDomain
}
s.SetRecvTimeout(v.ToNsecCapped())
return nil
}
if level == linux.SOL_SOCKET && name == linux.SO_SNDTIMEO {
if len(opt) < linux.SizeOfTimeval {
return syserr.ErrInvalidArgument
}
var v linux.Timeval
binary.Unmarshal(opt[:linux.SizeOfTimeval], usermem.ByteOrder, &v)
if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) {
return syserr.ErrDomain
}
s.SetSendTimeout(v.ToNsecCapped())
return nil
}
stack := t.NetworkContext().(*Stack)
id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_SetSockOpt{&pb.SetSockOptRequest{Fd: s.fd, Level: int64(level), Name: int64(name), Opt: opt}}}, false /* ignoreResult */)
@ -720,7 +746,7 @@ func rpcSendMsg(t *kernel.Task, req *pb.SyscallRequest_Sendmsg) (uint32, *syserr
}
// SendMsg implements socket.Socket.SendMsg.
func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, controlMessages socket.ControlMessages) (int, *syserr.Error) {
func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
// Whitelist flags.
if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_EOR|syscall.MSG_FASTOPEN|syscall.MSG_MORE|syscall.MSG_NOSIGNAL) != 0 {
return 0, syserr.ErrInvalidArgument
@ -787,7 +813,10 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
return int(totalWritten), nil
}
if err := t.Block(ch); err != nil {
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if err == syserror.ETIMEDOUT {
return int(totalWritten), syserr.ErrTryAgain
}
return int(totalWritten), syserr.FromError(err)
}
}

View File

@ -94,15 +94,23 @@ type Socket interface {
// ownership of the ControlMessage on error.
//
// If n > 0, err will either be nil or an error from t.Block.
SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, controlMessages ControlMessages) (n int, err *syserr.Error)
SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages ControlMessages) (n int, err *syserr.Error)
// SetRecvTimeout sets the timeout (in ns) for recv operations. Zero means
// no timeout.
// no timeout, and negative means DONTWAIT.
SetRecvTimeout(nanoseconds int64)
// RecvTimeout gets the current timeout (in ns) for recv operations. Zero
// means no timeout.
// means no timeout, and negative means DONTWAIT.
RecvTimeout() int64
// SetSendTimeout sets the timeout (in ns) for send operations. Zero means
// no timeout, and negative means DONTWAIT.
SetSendTimeout(nanoseconds int64)
// SendTimeout gets the current timeout (in ns) for send operations. Zero
// means no timeout, and negative means DONTWAIT.
SendTimeout() int64
}
// Provider is the interface implemented by providers of sockets for specific
@ -192,30 +200,45 @@ func NewDirent(ctx context.Context, d *device.Device) *fs.Dirent {
return fs.NewDirent(inode, fmt.Sprintf("socket:[%d]", ino))
}
// ReceiveTimeout stores a timeout for receive calls.
// SendReceiveTimeout stores timeouts for send and receive calls.
//
// It is meant to be embedded into Socket implementations to help satisfy the
// interface.
//
// Care must be taken when copying ReceiveTimeout as it contains atomic
// Care must be taken when copying SendReceiveTimeout as it contains atomic
// variables.
//
// +stateify savable
type ReceiveTimeout struct {
// ns is length of the timeout in nanoseconds.
type SendReceiveTimeout struct {
// send is length of the send timeout in nanoseconds.
//
// ns must be accessed atomically.
ns int64
// send must be accessed atomically.
send int64
// recv is length of the receive timeout in nanoseconds.
//
// recv must be accessed atomically.
recv int64
}
// SetRecvTimeout implements Socket.SetRecvTimeout.
func (rt *ReceiveTimeout) SetRecvTimeout(nanoseconds int64) {
atomic.StoreInt64(&rt.ns, nanoseconds)
func (to *SendReceiveTimeout) SetRecvTimeout(nanoseconds int64) {
atomic.StoreInt64(&to.recv, nanoseconds)
}
// RecvTimeout implements Socket.RecvTimeout.
func (rt *ReceiveTimeout) RecvTimeout() int64 {
return atomic.LoadInt64(&rt.ns)
func (to *SendReceiveTimeout) RecvTimeout() int64 {
return atomic.LoadInt64(&to.recv)
}
// SetSendTimeout implements Socket.SetSendTimeout.
func (to *SendReceiveTimeout) SetSendTimeout(nanoseconds int64) {
atomic.StoreInt64(&to.send, nanoseconds)
}
// SendTimeout implements Socket.SendTimeout.
func (to *SendReceiveTimeout) SendTimeout() int64 {
return atomic.LoadInt64(&to.send)
}
// GetSockOptEmitUnimplementedEvent emits unimplemented event if name is valid.

View File

@ -45,15 +45,16 @@ import (
//
// +stateify savable
type SocketOperations struct {
refs.AtomicRefCount
socket.ReceiveTimeout
fsutil.PipeSeek `state:"nosave"`
fsutil.NotDirReaddir `state:"nosave"`
fsutil.NoFsync `state:"nosave"`
fsutil.NoopFlush `state:"nosave"`
fsutil.NoMMap `state:"nosave"`
ep transport.Endpoint
isPacket bool
refs.AtomicRefCount
socket.SendReceiveTimeout
ep transport.Endpoint
isPacket bool
}
// New creates a new unix socket.
@ -367,7 +368,7 @@ func (s *SocketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IO
// SendMsg implements the linux syscall sendmsg(2) for unix sockets backed by
// a transport.Endpoint.
func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, controlMessages socket.ControlMessages) (int, *syserr.Error) {
func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
w := EndpointWriter{
Endpoint: s.ep,
Control: controlMessages.Unix,
@ -404,7 +405,10 @@ func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
break
}
if err := t.Block(ch); err != nil {
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
if err == syserror.ETIMEDOUT {
err = syserror.ErrWouldBlock
}
break
}
}

View File

@ -612,9 +612,11 @@ func RecvMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
var haveDeadline bool
var deadline ktime.Time
if dl := s.RecvTimeout(); dl != 0 {
if dl := s.RecvTimeout(); dl > 0 {
deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond)
haveDeadline = true
} else if dl < 0 {
flags |= linux.MSG_DONTWAIT
}
n, err := recvSingleMsg(t, s, msgPtr, flags, haveDeadline, deadline)
@ -671,10 +673,11 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
}
if !haveDeadline {
dl := s.RecvTimeout()
if dl != 0 {
if dl := s.RecvTimeout(); dl > 0 {
deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond)
haveDeadline = true
} else if dl < 0 {
flags |= linux.MSG_DONTWAIT
}
}
@ -821,10 +824,11 @@ func recvFrom(t *kernel.Task, fd kdefs.FD, bufPtr usermem.Addr, bufLen uint64, f
var haveDeadline bool
var deadline ktime.Time
if dl := s.RecvTimeout(); dl != 0 {
if dl := s.RecvTimeout(); dl > 0 {
deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond)
haveDeadline = true
} else if dl < 0 {
flags |= linux.MSG_DONTWAIT
}
n, sender, senderLen, cm, e := s.RecvMsg(t, dst, int(flags), haveDeadline, deadline, nameLenPtr != 0, 0)
@ -1001,8 +1005,17 @@ func sendSingleMsg(t *kernel.Task, s socket.Socket, file *fs.File, msgPtr userme
return 0, err
}
var haveDeadline bool
var deadline ktime.Time
if dl := s.SendTimeout(); dl > 0 {
deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond)
haveDeadline = true
} else if dl < 0 {
flags |= linux.MSG_DONTWAIT
}
// Call the syscall implementation.
n, e := s.SendMsg(t, src, to, int(flags), socket.ControlMessages{Unix: controlMessages})
n, e := s.SendMsg(t, src, to, int(flags), haveDeadline, deadline, socket.ControlMessages{Unix: controlMessages})
err = handleIOError(t, n != 0, e.ToError(), kernel.ERESTARTSYS, "sendmsg", file)
if err != nil {
controlMessages.Release()
@ -1052,8 +1065,17 @@ func sendTo(t *kernel.Task, fd kdefs.FD, bufPtr usermem.Addr, bufLen uint64, fla
return 0, err
}
var haveDeadline bool
var deadline ktime.Time
if dl := s.SendTimeout(); dl > 0 {
deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond)
haveDeadline = true
} else if dl < 0 {
flags |= linux.MSG_DONTWAIT
}
// Call the syscall implementation.
n, e := s.SendMsg(t, src, to, int(flags), socket.ControlMessages{Unix: control.New(t, s, nil)})
n, e := s.SendMsg(t, src, to, int(flags), haveDeadline, deadline, socket.ControlMessages{Unix: control.New(t, s, nil)})
return uintptr(n), handleIOError(t, n != 0, e.ToError(), kernel.ERESTARTSYS, "sendto", file)
}

View File

@ -332,6 +332,35 @@ TEST_P(AllSocketPairTest, RecvmsgTimeoutSucceeds) {
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, SendTimeoutAllowsSend) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 10
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[20] = {};
ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0),
SyscallSucceedsWithValue(sizeof(buf)));
}
TEST_P(AllSocketPairTest, SendmsgTimeoutAllowsSend) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 10
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[20] = {};
ASSERT_NO_FATAL_FAILURE(SendNullCmsg(sockets->first_fd(), buf, sizeof(buf)));
}
TEST_P(AllSocketPairTest, SoRcvTimeoIsSet) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
@ -382,6 +411,87 @@ TEST_P(AllSocketPairTest, RecvmsgTimeoutOneSecondSucceeds) {
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvTimeoutUsecTooLarge) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 2000000 // 2 seconds.
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
SyscallFailsWithErrno(EDOM));
}
TEST_P(AllSocketPairTest, SendTimeoutUsecTooLarge) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 2000000 // 2 seconds.
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
SyscallFailsWithErrno(EDOM));
}
TEST_P(AllSocketPairTest, RecvTimeoutUsecNeg) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = -1
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
SyscallFailsWithErrno(EDOM));
}
TEST_P(AllSocketPairTest, SendTimeoutUsecNeg) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = -1
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
SyscallFailsWithErrno(EDOM));
}
TEST_P(AllSocketPairTest, RecvTimeoutNegSec) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = -1, .tv_usec = 0
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[20] = {};
EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0),
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvmsgTimeoutNegSec) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = -1, .tv_usec = 0
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
struct msghdr msg = {};
char buf[20] = {};
struct iovec iov;
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0),
SyscallFailsWithErrno(EAGAIN));
}
TEST_P(AllSocketPairTest, RecvWaitAll) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

View File

@ -125,5 +125,27 @@ TEST_P(BlockingStreamSocketPairTest, RecvLessThanBufferWaitAll) {
EXPECT_GE(after - before, kDuration);
}
TEST_P(BlockingStreamSocketPairTest, SendTimeout) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 10
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[100] = {};
for (;;) {
int ret;
ASSERT_THAT(
ret = RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0),
::testing::AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(EAGAIN)));
if (ret == -1) {
break;
}
}
}
} // namespace testing
} // namespace gvisor

View File

@ -225,5 +225,27 @@ TEST_P(UnixNonStreamSocketPairTest, FragmentedRecvMsg) {
EXPECT_EQ(0, memcmp(write_buf.data(), ptr, buffer_size));
}
TEST_P(UnixNonStreamSocketPairTest, SendTimeout) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct timeval tv {
.tv_sec = 0, .tv_usec = 10
};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
SyscallSucceeds());
char buf[100] = {};
for (;;) {
int ret;
ASSERT_THAT(
ret = RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0),
::testing::AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(EAGAIN)));
if (ret == -1) {
break;
}
}
}
} // namespace testing
} // namespace gvisor