summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorBhasker Hariharan <bhaskerh@google.com>2020-08-20 13:23:21 -0700
committergVisor bot <gvisor-bot@google.com>2020-08-20 13:25:23 -0700
commitf12b545d8fc484f0bb48f280d161acb348263372 (patch)
treed4f7dbee79873eae865f8c6d2b0f0785c725808c /test
parent7ca62b9daa366ac2b3297f07de0ac601a2cca6bb (diff)
Skip listening TCP ports when trying to bind a free port.
PiperOrigin-RevId: 327686558
Diffstat (limited to 'test')
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc38
-rw-r--r--test/syscalls/linux/socket_inet_loopback_nogotsan.cc65
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound.cc25
3 files changed, 127 insertions, 1 deletions
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index c3b42682f..a62a10088 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -2573,6 +2573,44 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReservedReuseAddr) {
SyscallSucceeds());
}
+TEST_P(SocketMultiProtocolInetLoopbackTest,
+ MultipleBindsAllowedNoListeningReuseAddr) {
+ const auto& param = GetParam();
+ // UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP
+ // this is only permitted if there is no other listening socket.
+ SKIP_IF(param.type != SOCK_STREAM);
+ // Bind the v4 loopback on a v4 socket.
+ const TestAddress& test_addr = V4Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
+ test_addr.addr_len),
+ SyscallSucceeds());
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
+ &bound_addr_len),
+ SyscallSucceeds());
+
+ // Now create a socket and bind it to the same port, this should
+ // succeed since there is no listening socket for the same port.
+ FileDescriptor second_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(second_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(bind(second_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
+ test_addr.addr_len),
+ SyscallSucceeds());
+}
+
TEST_P(SocketMultiProtocolInetLoopbackTest, PortReuseTwoSockets) {
auto const& param = GetParam();
TestAddress const& test_addr = V4Loopback();
diff --git a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc b/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
index 791e2bd51..1a0b53394 100644
--- a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
+++ b/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
@@ -168,6 +168,71 @@ INSTANTIATE_TEST_SUITE_P(
TestParam{V6Loopback(), V6Loopback()}),
DescribeTestParam);
+struct ProtocolTestParam {
+ std::string description;
+ int type;
+};
+
+std::string DescribeProtocolTestParam(
+ ::testing::TestParamInfo<ProtocolTestParam> const& info) {
+ return info.param.description;
+}
+
+using SocketMultiProtocolInetLoopbackTest =
+ ::testing::TestWithParam<ProtocolTestParam>;
+
+TEST_P(SocketMultiProtocolInetLoopbackTest,
+ BindAvoidsListeningPortsReuseAddr_NoRandomSave) {
+ const auto& param = GetParam();
+ // UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP
+ // this is only permitted if there is no other listening socket.
+ SKIP_IF(param.type != SOCK_STREAM);
+
+ DisableSave ds; // Too many syscalls.
+
+ // A map of port to file descriptor binding the port.
+ std::map<uint16_t, FileDescriptor> listen_sockets;
+
+ // Exhaust all ephemeral ports.
+ while (true) {
+ // Bind the v4 loopback on a v4 socket.
+ TestAddress const& test_addr = V4Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ int ret = bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
+ test_addr.addr_len);
+ if (ret != 0) {
+ ASSERT_EQ(errno, EADDRINUSE);
+ break;
+ }
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
+ &bound_addr_len),
+ SyscallSucceeds());
+ uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port;
+
+ // Newly bound port should not already be in use by a listening socket.
+ ASSERT_EQ(listen_sockets.find(port), listen_sockets.end());
+ auto fd = bound_fd.get();
+ listen_sockets.insert(std::make_pair(port, std::move(bound_fd)));
+ ASSERT_THAT(listen(fd, SOMAXCONN), SyscallSucceeds());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllFamilies, SocketMultiProtocolInetLoopbackTest,
+ ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
+ ProtocolTestParam{"UDP", SOCK_DGRAM}),
+ DescribeProtocolTestParam);
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
index bc005e2bb..cdc9c2266 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
@@ -2121,7 +2121,7 @@ TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrReusePortDistribution) {
SyscallSucceedsWithValue(kMessageSize));
}
-// Check that connect returns EADDRNOTAVAIL when out of local ephemeral ports.
+// Check that connect returns EAGAIN when out of local ephemeral ports.
// We disable S/R because this test creates a large number of sockets.
TEST_P(IPv4UDPUnboundSocketTest, UDPConnectPortExhaustion_NoRandomSave) {
auto receiver1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -2154,6 +2154,29 @@ TEST_P(IPv4UDPUnboundSocketTest, UDPConnectPortExhaustion_NoRandomSave) {
}
}
+// Check that bind returns EADDRINUSE when out of local ephemeral ports.
+// We disable S/R because this test creates a large number of sockets.
+TEST_P(IPv4UDPUnboundSocketTest, UDPBindPortExhaustion_NoRandomSave) {
+ auto receiver1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ constexpr int kClients = 65536;
+ auto addr = V4Loopback();
+ // Disable cooperative S/R as we are making too many syscalls.
+ DisableSave ds;
+ std::vector<std::unique_ptr<FileDescriptor>> sockets;
+ for (int i = 0; i < kClients; i++) {
+ auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ int ret =
+ bind(s->get(), reinterpret_cast<sockaddr*>(&addr.addr), addr.addr_len);
+ if (ret == 0) {
+ sockets.push_back(std::move(s));
+ continue;
+ }
+ ASSERT_THAT(ret, SyscallFailsWithErrno(EADDRINUSE));
+ break;
+ }
+}
+
// Test that socket will receive packet info control message.
TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) {
// TODO(gvisor.dev/issue/1202): ioctl() is not supported by hostinet.