gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/socket_unix_non_stream.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 "test/syscalls/linux/socket_unix_non_stream.h"
    16  
    17  #include <stdio.h>
    18  #include <sys/mman.h>
    19  #include <sys/un.h>
    20  
    21  #include "gtest/gtest.h"
    22  #include "test/syscalls/linux/unix_domain_socket_test_util.h"
    23  #include "test/util/memory_util.h"
    24  #include "test/util/socket_util.h"
    25  #include "test/util/test_util.h"
    26  
    27  namespace gvisor {
    28  namespace testing {
    29  
    30  TEST_P(UnixNonStreamSocketPairTest, RecvMsgTooLarge) {
    31    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    32  
    33    int rcvbuf;
    34    socklen_t length = sizeof(rcvbuf);
    35    ASSERT_THAT(
    36        getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &rcvbuf, &length),
    37        SyscallSucceeds());
    38  
    39    // Make the call larger than the receive buffer.
    40    const int recv_size = 3 * rcvbuf;
    41  
    42    // Write a message that does fit in the receive buffer.
    43    const int write_size = rcvbuf - kPageSize;
    44  
    45    std::vector<char> write_buf(write_size, 'a');
    46    const int ret = RetryEINTR(write)(sockets->second_fd(), write_buf.data(),
    47                                      write_buf.size());
    48    if (ret < 0 && errno == ENOBUFS) {
    49      // NOTE(b/116636318): Linux may stall the write for a long time and
    50      // ultimately return ENOBUFS. Allow this error, since a retry will likely
    51      // result in the same error.
    52      return;
    53    }
    54    ASSERT_THAT(ret, SyscallSucceeds());
    55  
    56    std::vector<char> recv_buf(recv_size);
    57  
    58    ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->first_fd(), recv_buf.data(),
    59                                       recv_buf.size(), write_size));
    60  
    61    recv_buf.resize(write_size);
    62    EXPECT_EQ(recv_buf, write_buf);
    63  }
    64  
    65  // Create a region of anonymous memory of size 'size', which is fragmented in
    66  // FileMem.
    67  //
    68  // ptr contains the start address of the region. The returned vector contains
    69  // all of the mappings to be unmapped when done.
    70  PosixErrorOr<std::vector<Mapping>> CreateFragmentedRegion(const int size,
    71                                                            void** ptr) {
    72    Mapping region;
    73    ASSIGN_OR_RETURN_ERRNO(region, Mmap(nullptr, size, PROT_NONE,
    74                                        MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
    75  
    76    *ptr = region.ptr();
    77  
    78    // Don't save hundreds of times for all of these mmaps.
    79    DisableSave ds;
    80  
    81    std::vector<Mapping> pages;
    82  
    83    // Map and commit a single page at a time, mapping and committing an unrelated
    84    // page between each call to force FileMem fragmentation.
    85    for (uintptr_t addr = region.addr(); addr < region.endaddr();
    86         addr += kPageSize) {
    87      Mapping page;
    88      ASSIGN_OR_RETURN_ERRNO(
    89          page,
    90          Mmap(reinterpret_cast<void*>(addr), kPageSize, PROT_READ | PROT_WRITE,
    91               MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));
    92      *reinterpret_cast<volatile char*>(page.ptr()) = 42;
    93  
    94      pages.emplace_back(std::move(page));
    95  
    96      // Unrelated page elsewhere.
    97      ASSIGN_OR_RETURN_ERRNO(page,
    98                             Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE,
    99                                  MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
   100      *reinterpret_cast<volatile char*>(page.ptr()) = 42;
   101  
   102      pages.emplace_back(std::move(page));
   103    }
   104  
   105    // The mappings above have taken ownership of the region.
   106    region.release();
   107  
   108    return std::move(pages);
   109  }
   110  
   111  // A contiguous iov that is heavily fragmented in FileMem can still be sent
   112  // successfully. See b/115833655.
   113  TEST_P(UnixNonStreamSocketPairTest, FragmentedSendMsg) {
   114    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
   115  
   116    const int buffer_size = UIO_MAXIOV * kPageSize;
   117    // Extra page for message header overhead.
   118    const int sndbuf = buffer_size + kPageSize;
   119    // N.B. setsockopt(SO_SNDBUF) doubles the passed value.
   120    const int set_sndbuf = sndbuf / 2;
   121  
   122    EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF,
   123                           &set_sndbuf, sizeof(set_sndbuf)),
   124                SyscallSucceeds());
   125  
   126    int actual_sndbuf = 0;
   127    socklen_t length = sizeof(actual_sndbuf);
   128    ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF,
   129                           &actual_sndbuf, &length),
   130                SyscallSucceeds());
   131  
   132    if (actual_sndbuf != sndbuf) {
   133      // Unable to get the sndbuf we want.
   134      //
   135      // N.B. At minimum, the socketpair gofer should provide a socket that is
   136      // already the correct size.
   137      //
   138      // TODO(b/35921550): When internal UDS support SO_SNDBUF, we can assert that
   139      // we always get the right SO_SNDBUF on gVisor.
   140      GTEST_SKIP() << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf;
   141    }
   142  
   143    // Create a contiguous region of memory of 2*UIO_MAXIOV*PAGE_SIZE. We'll call
   144    // sendmsg with a single iov, but the goal is to get the sentry to split this
   145    // into > UIO_MAXIOV iovs when calling the kernel.
   146    void* ptr;
   147    std::vector<Mapping> pages =
   148        ASSERT_NO_ERRNO_AND_VALUE(CreateFragmentedRegion(buffer_size, &ptr));
   149  
   150    struct iovec iov = {};
   151    iov.iov_base = ptr;
   152    iov.iov_len = buffer_size;
   153  
   154    struct msghdr msg = {};
   155    msg.msg_iov = &iov;
   156    msg.msg_iovlen = 1;
   157  
   158    // NOTE(b/116636318,b/115833655): Linux has poor behavior in the presence of
   159    // physical memory fragmentation. As a result, this may stall for a long time
   160    // and ultimately return ENOBUFS. Allow this error, since it means that we
   161    // made it to the host kernel and started the sendmsg.
   162    EXPECT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0),
   163                AnyOf(SyscallSucceedsWithValue(buffer_size),
   164                      SyscallFailsWithErrno(ENOBUFS)));
   165  }
   166  
   167  // A contiguous iov that is heavily fragmented in FileMem can still be received
   168  // into successfully. Regression test for b/115833655.
   169  TEST_P(UnixNonStreamSocketPairTest, FragmentedRecvMsg) {
   170    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
   171  
   172    const int buffer_size = UIO_MAXIOV * kPageSize;
   173    // Extra page for message header overhead.
   174    const int sndbuf = buffer_size + kPageSize;
   175    // N.B. setsockopt(SO_SNDBUF) doubles the passed value.
   176    const int set_sndbuf = sndbuf / 2;
   177  
   178    EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF,
   179                           &set_sndbuf, sizeof(set_sndbuf)),
   180                SyscallSucceeds());
   181  
   182    int actual_sndbuf = 0;
   183    socklen_t length = sizeof(actual_sndbuf);
   184    ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF,
   185                           &actual_sndbuf, &length),
   186                SyscallSucceeds());
   187  
   188    if (actual_sndbuf != sndbuf) {
   189      // Unable to get the sndbuf we want.
   190      //
   191      // N.B. At minimum, the socketpair gofer should provide a socket that is
   192      // already the correct size.
   193      //
   194      // TODO(b/35921550): When internal UDS support SO_SNDBUF, we can assert that
   195      // we always get the right SO_SNDBUF on gVisor.
   196      GTEST_SKIP() << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf;
   197    }
   198  
   199    std::vector<char> write_buf(buffer_size, 'a');
   200    const int ret = RetryEINTR(write)(sockets->first_fd(), write_buf.data(),
   201                                      write_buf.size());
   202    if (ret < 0 && errno == ENOBUFS) {
   203      // NOTE(b/116636318): Linux may stall the write for a long time and
   204      // ultimately return ENOBUFS. Allow this error, since a retry will likely
   205      // result in the same error.
   206      return;
   207    }
   208    ASSERT_THAT(ret, SyscallSucceeds());
   209  
   210    // Create a contiguous region of memory of 2*UIO_MAXIOV*PAGE_SIZE. We'll call
   211    // sendmsg with a single iov, but the goal is to get the sentry to split this
   212    // into > UIO_MAXIOV iovs when calling the kernel.
   213    void* ptr;
   214    std::vector<Mapping> pages =
   215        ASSERT_NO_ERRNO_AND_VALUE(CreateFragmentedRegion(buffer_size, &ptr));
   216  
   217    ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(
   218        sockets->second_fd(), reinterpret_cast<char*>(ptr), buffer_size));
   219  
   220    EXPECT_EQ(0, memcmp(write_buf.data(), ptr, buffer_size));
   221  }
   222  
   223  TEST_P(UnixNonStreamSocketPairTest, SendTimeout) {
   224    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
   225  
   226    struct timeval tv {
   227      .tv_sec = 0, .tv_usec = 10
   228    };
   229    EXPECT_THAT(
   230        setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
   231        SyscallSucceeds());
   232  
   233    const int buf_size = 5 * kPageSize;
   234    EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &buf_size,
   235                           sizeof(buf_size)),
   236                SyscallSucceeds());
   237    EXPECT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_RCVBUF, &buf_size,
   238                           sizeof(buf_size)),
   239                SyscallSucceeds());
   240  
   241    // The buffer size should be big enough to avoid many iterations in the next
   242    // loop. Otherwise, this will slow down save tests.
   243    std::vector<char> buf(kPageSize);
   244    for (;;) {
   245      int ret;
   246      ASSERT_THAT(
   247          ret = RetryEINTR(send)(sockets->first_fd(), buf.data(), buf.size(), 0),
   248          ::testing::AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(EAGAIN)));
   249      if (ret == -1) {
   250        break;
   251      }
   252    }
   253  }
   254  
   255  }  // namespace testing
   256  }  // namespace gvisor