From 22bc54453d8c4a505c515d44812a9e08c83ea1fb Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Sun, 10 Jul 2022 18:09:36 -0700 Subject: [PATCH] Add shutdown-while-synrcvd test Updates #6666. PiperOrigin-RevId: 460105271 --- test/syscalls/linux/tcp_socket.cc | 101 +++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 602845ae5..6ebe4ebfc 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -1050,7 +1050,7 @@ TEST_P(SimpleTcpSocketTest, NonBlockingConnectNoListener) { socklen_t optlen = sizeof(err); ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), SyscallSucceeds()); - ASSERT_THAT(optlen, sizeof(err)); + ASSERT_EQ(optlen, sizeof(err)); EXPECT_EQ(err, ECONNREFUSED); unsigned char c; @@ -1324,6 +1324,7 @@ void NonBlockingConnect(int family, int16_t pollMask) { socklen_t optlen = sizeof(err); ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), SyscallSucceeds()); + ASSERT_EQ(optlen, sizeof(err)); EXPECT_EQ(err, 0); @@ -1664,6 +1665,7 @@ TEST_P(SimpleTcpSocketTest, SetMaxSeg) { socklen_t optlen = sizeof(optval); ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_MAXSEG, &optval, &optlen), SyscallSucceedsWithValue(0)); + ASSERT_EQ(optlen, sizeof(optval)); EXPECT_EQ(kDefaultMSS, optval); EXPECT_EQ(sizeof(optval), optlen); @@ -2256,6 +2258,103 @@ TEST_P(SimpleTcpSocketTest, OnlyAcknowledgeBacklogConnections) { } } +TEST_P(SimpleTcpSocketTest, SynRcvdOnListenerShutdown) { + FileDescriptor bound_s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + sockaddr_storage bound_addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t bound_addrlen = sizeof(bound_addr); + + ASSERT_THAT(bind(bound_s.get(), AsSockAddr(&bound_addr), bound_addrlen), + SyscallSucceeds()); + + // Get the addresses the socket is bound to because the port is chosen by the + // stack. + ASSERT_THAT( + getsockname(bound_s.get(), AsSockAddr(&bound_addr), &bound_addrlen), + SyscallSucceeds()); + + // kBacklog connections are permitted to be in the SYNRCVD state. Select the + // largest reasonable value; we want to create a situation where at least some + // of the connections are still in SYNRCVD when we shut down the listener. + constexpr int kBacklog = 256; + ASSERT_THAT(listen(bound_s.get(), kBacklog), SyscallSucceeds()); + + std::array threads; + for (auto& thread : threads) { + FileDescriptor connecting_s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(GetParam(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); + ASSERT_THAT(connect(connecting_s.get(), + reinterpret_cast(&bound_addr), + bound_addrlen), + SyscallFailsWithErrno(EINPROGRESS)); + thread = std::thread([connecting_s = std::move(connecting_s)]() { + struct pollfd poll_fd = { + .fd = connecting_s.get(), + }; + poll_fd.events = std::numeric_limits::max(); + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, 1000), + SyscallSucceedsWithValue(1)); + + int err; + socklen_t optlen = sizeof(err); + ASSERT_THAT( + getsockopt(connecting_s.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), + SyscallSucceeds()); + ASSERT_EQ(optlen, sizeof(err)); + + if (err == 0) { + EXPECT_EQ(poll_fd.revents, POLLOUT | POLLWRNORM); + } else { + EXPECT_THAT(err, ::testing::AnyOf(::testing::Eq(ECONNRESET), + ::testing::Eq(ECONNREFUSED))) + << strerror(err); + + const int revents = poll_fd.revents; + + // It's possible the error arrived *after* poll returned. Fetch the + // signals again - this time with a zero timeout. + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), + SyscallSucceedsWithValue(1)); + + EXPECT_EQ(poll_fd.revents, []() { + const int expected_revents = + POLLIN | POLLOUT | POLLHUP | POLLRDNORM | POLLWRNORM; + // TODO(gvisor.dev/issue/6666): POLLERR is still present after + // getsockopt(..., SO_ERROR, ...) call. + if (IsRunningOnGvisor()) { + return expected_revents | POLLPRI | POLLERR; + } else { + return expected_revents | POLLRDHUP; + } + }()); + + EXPECT_THAT( + revents, + ::testing::AnyOf( + // If the error arrived after poll returned. + ::testing::Eq(POLLOUT | POLLWRNORM), + ::testing::Eq([expected_revents = poll_fd.revents]() -> int { + // TODO(gvisor.dev/issue/6666): on Linux, POLLERR goes away + // after the getsockopt(..., SO_ERROR, ...) call, but not on + // gVisor. + if (IsRunningOnGvisor()) { + return expected_revents; + } + return expected_revents | POLLERR; + }()))); + } + }); + } + + EXPECT_THAT(shutdown(bound_s.get(), SHUT_RD), SyscallSucceeds()); + + for (auto& thread : threads) { + thread.join(); + } +} + // Tests that send will return EWOULDBLOCK initially with large buffer and will // succeed after the send buffer size is increased. TEST_P(TcpSocketTest, SendUnblocksOnSendBufferIncrease) {