gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/socket_inet_loopback_nogotsan.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <arpa/inet.h>
    16  #include <netinet/in.h>
    17  #include <netinet/tcp.h>
    18  #include <string.h>
    19  
    20  #include <iostream>
    21  #include <memory>
    22  #include <string>
    23  #include <utility>
    24  #include <vector>
    25  
    26  #include "gmock/gmock.h"
    27  #include "gtest/gtest.h"
    28  #include "absl/strings/str_cat.h"
    29  #include "test/syscalls/linux/ip_socket_test_util.h"
    30  #include "test/syscalls/linux/socket_inet_loopback_test_params.h"
    31  #include "test/util/file_descriptor.h"
    32  #include "test/util/posix_error.h"
    33  #include "test/util/save_util.h"
    34  #include "test/util/socket_util.h"
    35  #include "test/util/test_util.h"
    36  
    37  namespace gvisor {
    38  namespace testing {
    39  
    40  namespace {
    41  
    42  using SocketInetLoopbackTest = ::testing::TestWithParam<SocketInetTestParam>;
    43  
    44  // This test verifies that connect returns EADDRNOTAVAIL if all local ephemeral
    45  // ports are already in use for a given destination ip/port.
    46  //
    47  // We disable S/R because this test creates a large number of sockets.
    48  //
    49  // FIXME(b/162475855): This test is failing reliably.
    50  TEST_P(SocketInetLoopbackTest, DISABLED_TestTCPPortExhaustion) {
    51    SocketInetTestParam const& param = GetParam();
    52    TestAddress const& listener = param.listener;
    53    TestAddress const& connector = param.connector;
    54  
    55    constexpr int kBacklog = 10;
    56    constexpr int kClients = 65536;
    57  
    58    // Create the listening socket.
    59    auto listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
    60        Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
    61    sockaddr_storage listen_addr = listener.addr;
    62    ASSERT_THAT(
    63        bind(listen_fd.get(), AsSockAddr(&listen_addr), listener.addr_len),
    64        SyscallSucceeds());
    65    ASSERT_THAT(listen(listen_fd.get(), kBacklog), SyscallSucceeds());
    66  
    67    // Get the port bound by the listening socket.
    68    socklen_t addrlen = listener.addr_len;
    69    ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(&listen_addr), &addrlen),
    70                SyscallSucceeds());
    71    uint16_t const port =
    72        ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
    73  
    74    // Disable cooperative S/R as we are making too many syscalls.
    75    DisableSave ds;
    76  
    77    // Now we keep opening connections till we run out of local ephemeral ports.
    78    // and assert the error we get back.
    79    sockaddr_storage conn_addr = connector.addr;
    80    ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
    81    std::vector<FileDescriptor> clients;
    82    std::vector<FileDescriptor> servers;
    83  
    84    for (int i = 0; i < kClients; i++) {
    85      FileDescriptor client = ASSERT_NO_ERRNO_AND_VALUE(
    86          Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
    87      int ret = connect(client.get(), AsSockAddr(&conn_addr), connector.addr_len);
    88      if (ret == 0) {
    89        clients.push_back(std::move(client));
    90        FileDescriptor server =
    91            ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
    92        servers.push_back(std::move(server));
    93        continue;
    94      }
    95      ASSERT_THAT(ret, SyscallFailsWithErrno(EADDRNOTAVAIL));
    96      break;
    97    }
    98  }
    99  
   100  INSTANTIATE_TEST_SUITE_P(All, SocketInetLoopbackTest,
   101                           SocketInetLoopbackTestValues(),
   102                           DescribeSocketInetTestParam);
   103  
   104  using SocketMultiProtocolInetLoopbackTest =
   105      ::testing::TestWithParam<ProtocolTestParam>;
   106  
   107  TEST_P(SocketMultiProtocolInetLoopbackTest,
   108         TCPBindAvoidsOtherBoundPortsReuseAddr) {
   109    ProtocolTestParam const& param = GetParam();
   110    // UDP sockets are allowed to bind/listen on an already bound port w/
   111    // SO_REUSEADDR even when requesting a port from the kernel. In case of TCP
   112    // rebinding is only permitted when SO_REUSEADDR is set and an explicit port
   113    // is specified. When a zero port is specified to the bind() call then an
   114    // already bound port will not be picked.
   115    SKIP_IF(param.type != SOCK_STREAM);
   116  
   117    DisableSave ds;  // Too many syscalls.
   118  
   119    // A map of port to file descriptor binding the port.
   120    std::map<uint16_t, FileDescriptor> bound_sockets;
   121  
   122    // Reduce number of ephemeral ports if permitted to reduce running time of
   123    // the test.
   124    [[maybe_unused]] const int nports =
   125        ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
   126  
   127    // Exhaust all ephemeral ports.
   128    while (true) {
   129      // Bind the v4 loopback on a v4 socket.
   130      TestAddress const& test_addr = V4Loopback();
   131      sockaddr_storage bound_addr = test_addr.addr;
   132      FileDescriptor bound_fd =
   133          ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
   134  
   135      ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR,
   136                             &kSockOptOn, sizeof(kSockOptOn)),
   137                  SyscallSucceeds());
   138  
   139      int ret = bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len);
   140      if (ret != 0) {
   141        ASSERT_EQ(errno, EADDRINUSE);
   142        break;
   143      }
   144      // Get the port that we bound.
   145      socklen_t bound_addr_len = test_addr.addr_len;
   146      ASSERT_THAT(
   147          getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
   148          SyscallSucceeds());
   149      uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port;
   150  
   151      auto [iter, inserted] = bound_sockets.emplace(port, std::move(bound_fd));
   152      ASSERT_TRUE(inserted);
   153    }
   154  }
   155  
   156  TEST_P(SocketMultiProtocolInetLoopbackTest,
   157         UDPBindMayBindOtherBoundPortsReuseAddr) {
   158    ProtocolTestParam const& param = GetParam();
   159    // UDP sockets are allowed to bind/listen on an already bound port w/
   160    // SO_REUSEADDR even when requesting a port from the kernel.
   161    SKIP_IF(param.type != SOCK_DGRAM);
   162  
   163    DisableSave ds;  // Too many syscalls.
   164  
   165    // A map of port to file descriptor binding the port.
   166    std::map<uint16_t, FileDescriptor> bound_sockets;
   167  
   168    // Reduce number of ephemeral ports if permitted to reduce running time of
   169    // the test.
   170    [[maybe_unused]] const int nports =
   171        ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
   172  
   173    // Exhaust all ephemeral ports.
   174    bool duplicate_binding = false;
   175    while (true) {
   176      // Bind the v4 loopback on a v4 socket.
   177      TestAddress const& test_addr = V4Loopback();
   178      sockaddr_storage bound_addr = test_addr.addr;
   179      FileDescriptor bound_fd =
   180          ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
   181  
   182      ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR,
   183                             &kSockOptOn, sizeof(kSockOptOn)),
   184                  SyscallSucceeds());
   185  
   186      ASSERT_THAT(
   187          bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
   188          SyscallSucceeds());
   189  
   190      // Get the port that we bound.
   191      socklen_t bound_addr_len = test_addr.addr_len;
   192      ASSERT_THAT(
   193          getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
   194          SyscallSucceeds());
   195      uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port;
   196  
   197      auto [iter, inserted] = bound_sockets.emplace(port, std::move(bound_fd));
   198      if (!inserted) {
   199        duplicate_binding = true;
   200        break;
   201      }
   202    }
   203    ASSERT_TRUE(duplicate_binding);
   204  }
   205  
   206  INSTANTIATE_TEST_SUITE_P(AllFamilies, SocketMultiProtocolInetLoopbackTest,
   207                           ProtocolTestValues(), DescribeProtocolTestParam);
   208  
   209  }  // namespace
   210  
   211  }  // namespace testing
   212  }  // namespace gvisor