Add a syscall test for SO_LINGER with timeout zero.

The test checks the read and write behavior when SO_LINGER is set for zero. The
socket should send a RST immediately when SO_LINGER is set to zero and close()
is called.

From the first fd which sets SO_LINGER to zero:
- send the data and then call close. Read should succeed on the second fd.
- close the first fd and then send data from the second fd. Write should not
succeed as the connection is reset as soon as the close is called.

PiperOrigin-RevId: 456797570
This commit is contained in:
Nayana Bidari
2022-06-23 10:04:47 -07:00
committed by gVisor bot
parent a6d53a75ca
commit 102e3d4993
@@ -1360,5 +1360,59 @@ TEST_P(TCPSocketPairTest, CloseWithLingerOption) {
ASSERT_THAT(RetryEINTR(write)(dupFd.get(), buf, sizeof(buf)),
SyscallFailsWithErrno(EBADF));
}
TEST_P(TCPSocketPairTest, ResetWithSoLingerZeroTimeoutOption) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Check getsockopt before SO_LINGER option is set.
struct linger got_linger = {-1, -1};
socklen_t got_len = sizeof(got_linger);
ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
&got_linger, &got_len),
SyscallSucceeds());
ASSERT_THAT(got_len, sizeof(got_linger));
struct linger want_linger = {};
EXPECT_EQ(0, memcmp(&want_linger, &got_linger, got_len));
char buf[10] = {};
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
SyscallSucceedsWithValue(sizeof(buf)));
// Set and get SO_LINGER with zero timeout.
struct linger sl;
sl.l_onoff = 1;
sl.l_linger = 0;
ASSERT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
SyscallSucceeds());
ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
&got_linger, &got_len),
SyscallSucceeds());
ASSERT_EQ(got_len, sizeof(got_linger));
EXPECT_EQ(sl.l_onoff, got_linger.l_onoff);
EXPECT_EQ(sl.l_linger, got_linger.l_linger);
// Wait until the socket sees the data on its side but don't read it.
struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0};
constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data.
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
SyscallSucceedsWithValue(1));
ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds());
// Attempt to write, but not possible because of connection reset.
poll_fd = {sockets->second_fd(), POLLHUP, 0};
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
SyscallSucceedsWithValue(1));
char buffer[10] = {};
ASSERT_THAT(RetryEINTR(write)(sockets->second_fd(), buffer, sizeof(buffer)),
SyscallFailsWithErrno(ECONNRESET));
ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)),
SyscallSucceedsWithValue(sizeof(buf)));
}
} // namespace testing
} // namespace gvisor