diff options
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_read.go | 23 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_write.go | 23 | ||||
-rw-r--r-- | test/syscalls/linux/socket_generic.cc | 59 |
3 files changed, 96 insertions, 9 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_read.go b/pkg/sentry/syscalls/linux/sys_read.go index b6df4d9d4..8105e9b43 100644 --- a/pkg/sentry/syscalls/linux/sys_read.go +++ b/pkg/sentry/syscalls/linux/sys_read.go @@ -15,11 +15,15 @@ package linux import ( + "time" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" "gvisor.googlesource.com/gvisor/pkg/syserror" "gvisor.googlesource.com/gvisor/pkg/waiter" @@ -259,6 +263,20 @@ func readv(t *kernel.Task, f *fs.File, dst usermem.IOSequence) (int64, error) { return n, err } + // Sockets support read timeouts. + var haveDeadline bool + var deadline ktime.Time + if s, ok := f.FileOperations.(socket.Socket); ok { + dl := s.RecvTimeout() + if dl < 0 && err == syserror.ErrWouldBlock { + return n, err + } + if dl > 0 { + deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond) + haveDeadline = true + } + } + // Register for notifications. w, ch := waiter.NewChannelEntry(nil) f.EventRegister(&w, EventMaskRead) @@ -277,7 +295,10 @@ func readv(t *kernel.Task, f *fs.File, dst usermem.IOSequence) (int64, error) { } // Wait for a notification that we should retry. - if err = t.Block(ch); err != nil { + if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + err = syserror.ErrWouldBlock + } break } } diff --git a/pkg/sentry/syscalls/linux/sys_write.go b/pkg/sentry/syscalls/linux/sys_write.go index 750a098cd..a5ad7efb2 100644 --- a/pkg/sentry/syscalls/linux/sys_write.go +++ b/pkg/sentry/syscalls/linux/sys_write.go @@ -15,11 +15,15 @@ package linux import ( + "time" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" "gvisor.googlesource.com/gvisor/pkg/syserror" "gvisor.googlesource.com/gvisor/pkg/waiter" @@ -263,6 +267,20 @@ func writev(t *kernel.Task, f *fs.File, src usermem.IOSequence) (int64, error) { return n, err } + // Sockets support write timeouts. + var haveDeadline bool + var deadline ktime.Time + if s, ok := f.FileOperations.(socket.Socket); ok { + dl := s.SendTimeout() + if dl < 0 && err == syserror.ErrWouldBlock { + return n, err + } + if dl > 0 { + deadline = t.Kernel().MonotonicClock().Now().Add(time.Duration(dl) * time.Nanosecond) + haveDeadline = true + } + } + // Register for notifications. w, ch := waiter.NewChannelEntry(nil) f.EventRegister(&w, EventMaskWrite) @@ -281,7 +299,10 @@ func writev(t *kernel.Task, f *fs.File, src usermem.IOSequence) (int64, error) { } // Wait for a notification that we should retry. - if err = t.Block(ch); err != nil { + if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + err = syserror.ErrWouldBlock + } break } } diff --git a/test/syscalls/linux/socket_generic.cc b/test/syscalls/linux/socket_generic.cc index c65b29112..974c0dd7b 100644 --- a/test/syscalls/linux/socket_generic.cc +++ b/test/syscalls/linux/socket_generic.cc @@ -280,7 +280,22 @@ TEST_P(AllSocketPairTest, SndBufSucceeds) { EXPECT_GT(size, 0); } -TEST_P(AllSocketPairTest, RecvTimeoutSucceeds) { +TEST_P(AllSocketPairTest, RecvTimeoutReadSucceeds) { + 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_RCVTIMEO, &tv, sizeof(tv)), + SyscallSucceeds()); + + char buf[20] = {}; + EXPECT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(AllSocketPairTest, RecvTimeoutRecvSucceeds) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); struct timeval tv { @@ -295,7 +310,7 @@ TEST_P(AllSocketPairTest, RecvTimeoutSucceeds) { SyscallFailsWithErrno(EAGAIN)); } -TEST_P(AllSocketPairTest, RecvTimeoutOneSecondSucceeds) { +TEST_P(AllSocketPairTest, RecvTimeoutRecvOneSecondSucceeds) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); struct timeval tv { @@ -310,7 +325,7 @@ TEST_P(AllSocketPairTest, RecvTimeoutOneSecondSucceeds) { SyscallFailsWithErrno(EAGAIN)); } -TEST_P(AllSocketPairTest, RecvmsgTimeoutSucceeds) { +TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgSucceeds) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); struct timeval tv { @@ -332,6 +347,21 @@ TEST_P(AllSocketPairTest, RecvmsgTimeoutSucceeds) { SyscallFailsWithErrno(EAGAIN)); } +TEST_P(AllSocketPairTest, SendTimeoutAllowsWrite) { + 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(write)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); +} + TEST_P(AllSocketPairTest, SendTimeoutAllowsSend) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -347,7 +377,7 @@ TEST_P(AllSocketPairTest, SendTimeoutAllowsSend) { SyscallSucceedsWithValue(sizeof(buf))); } -TEST_P(AllSocketPairTest, SendmsgTimeoutAllowsSend) { +TEST_P(AllSocketPairTest, SendTimeoutAllowsSendmsg) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); struct timeval tv { @@ -389,7 +419,7 @@ TEST_P(AllSocketPairTest, SoRcvTimeoIsSetLargerArg) { SyscallSucceeds()); } -TEST_P(AllSocketPairTest, RecvmsgTimeoutOneSecondSucceeds) { +TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgOneSecondSucceeds) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); struct timeval tv { @@ -455,7 +485,22 @@ TEST_P(AllSocketPairTest, SendTimeoutUsecNeg) { SyscallFailsWithErrno(EDOM)); } -TEST_P(AllSocketPairTest, RecvTimeoutNegSec) { +TEST_P(AllSocketPairTest, RecvTimeoutNegSecRead) { + 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(read)(sockets->first_fd(), buf, sizeof(buf)), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecv) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); struct timeval tv { @@ -470,7 +515,7 @@ TEST_P(AllSocketPairTest, RecvTimeoutNegSec) { SyscallFailsWithErrno(EAGAIN)); } -TEST_P(AllSocketPairTest, RecvmsgTimeoutNegSec) { +TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecvmsg) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); struct timeval tv { |