github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/partial_bad_buffer.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 <errno.h>
    16  #include <fcntl.h>
    17  #include <netinet/in.h>
    18  #include <netinet/tcp.h>
    19  #include <sys/mman.h>
    20  #include <sys/socket.h>
    21  #include <sys/stat.h>
    22  #include <sys/syscall.h>
    23  #include <sys/types.h>
    24  #include <sys/uio.h>
    25  #include <unistd.h>
    26  
    27  #include "gtest/gtest.h"
    28  #include "absl/time/clock.h"
    29  #include "test/syscalls/linux/socket_test_util.h"
    30  #include "test/util/file_descriptor.h"
    31  #include "test/util/fs_util.h"
    32  #include "test/util/posix_error.h"
    33  #include "test/util/temp_path.h"
    34  #include "test/util/test_util.h"
    35  
    36  using ::testing::Gt;
    37  
    38  namespace gvisor {
    39  namespace testing {
    40  
    41  namespace {
    42  
    43  constexpr char kMessage[] = "hello world";
    44  
    45  // PartialBadBufferTest checks the result of various IO syscalls when passed a
    46  // buffer that does not have the space specified in the syscall (most of it is
    47  // PROT_NONE). Linux is annoyingly inconsistent among different syscalls, so we
    48  // test all of them.
    49  class PartialBadBufferTest : public ::testing::Test {
    50   protected:
    51    void SetUp() override {
    52      // Create and open a directory for getdents cases.
    53      directory_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
    54      ASSERT_THAT(
    55          directory_fd_ = open(directory_.path().c_str(), O_RDONLY | O_DIRECTORY),
    56          SyscallSucceeds());
    57  
    58      // Create and open a normal file, placing it in the directory
    59      // so the getdents cases have some dirents.
    60      name_ = JoinPath(directory_.path(), "a");
    61      ASSERT_THAT(fd_ = open(name_.c_str(), O_RDWR | O_CREAT, 0644),
    62                  SyscallSucceeds());
    63  
    64      // Write some initial data.
    65      size_t size = sizeof(kMessage) - 1;
    66      EXPECT_THAT(WriteFd(fd_, &kMessage, size), SyscallSucceedsWithValue(size));
    67      ASSERT_THAT(lseek(fd_, 0, SEEK_SET), SyscallSucceeds());
    68  
    69      // Map a useable buffer.
    70      addr_ = mmap(0, 2 * kPageSize, PROT_READ | PROT_WRITE,
    71                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    72      ASSERT_NE(addr_, MAP_FAILED);
    73      char* buf = reinterpret_cast<char*>(addr_);
    74  
    75      // Guard page for our read to run into.
    76      ASSERT_THAT(mprotect(reinterpret_cast<void*>(buf + kPageSize), kPageSize,
    77                           PROT_NONE),
    78                  SyscallSucceeds());
    79  
    80      // Leave only one free byte in the buffer.
    81      bad_buffer_ = buf + kPageSize - 1;
    82    }
    83  
    84    off_t Size() {
    85      struct stat st;
    86      int rc = fstat(fd_, &st);
    87      if (rc < 0) {
    88        return static_cast<off_t>(rc);
    89      }
    90      return st.st_size;
    91    }
    92  
    93    void TearDown() override {
    94      EXPECT_THAT(munmap(addr_, 2 * kPageSize), SyscallSucceeds()) << addr_;
    95      EXPECT_THAT(close(fd_), SyscallSucceeds());
    96      EXPECT_THAT(unlink(name_.c_str()), SyscallSucceeds());
    97      EXPECT_THAT(close(directory_fd_), SyscallSucceeds());
    98    }
    99  
   100    // Return buffer with n bytes of free space.
   101    // N.B. this is the same buffer used to back bad_buffer_.
   102    char* FreeBytes(size_t n) {
   103      TEST_CHECK(n <= static_cast<size_t>(4096));
   104      return reinterpret_cast<char*>(addr_) + kPageSize - n;
   105    }
   106  
   107    std::string name_;
   108    int fd_;
   109    TempPath directory_;
   110    int directory_fd_;
   111    void* addr_;
   112    char* bad_buffer_;
   113  };
   114  
   115  // We do both "big" and "small" tests to try to hit the "zero copy" and
   116  // non-"zero copy" paths, which have different code paths for handling faults.
   117  
   118  TEST_F(PartialBadBufferTest, ReadBig) {
   119    EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, kPageSize),
   120                SyscallSucceedsWithValue(1));
   121    EXPECT_EQ('h', bad_buffer_[0]);
   122  }
   123  
   124  TEST_F(PartialBadBufferTest, ReadSmall) {
   125    EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, 10),
   126                SyscallSucceedsWithValue(1));
   127    EXPECT_EQ('h', bad_buffer_[0]);
   128  }
   129  
   130  TEST_F(PartialBadBufferTest, PreadBig) {
   131    EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, kPageSize, 0),
   132                SyscallSucceedsWithValue(1));
   133    EXPECT_EQ('h', bad_buffer_[0]);
   134  }
   135  
   136  TEST_F(PartialBadBufferTest, PreadSmall) {
   137    EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, 10, 0),
   138                SyscallSucceedsWithValue(1));
   139    EXPECT_EQ('h', bad_buffer_[0]);
   140  }
   141  
   142  TEST_F(PartialBadBufferTest, ReadvBig) {
   143    struct iovec vec;
   144    vec.iov_base = bad_buffer_;
   145    vec.iov_len = kPageSize;
   146  
   147    EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1));
   148    EXPECT_EQ('h', bad_buffer_[0]);
   149  }
   150  
   151  TEST_F(PartialBadBufferTest, ReadvSmall) {
   152    struct iovec vec;
   153    vec.iov_base = bad_buffer_;
   154    vec.iov_len = 10;
   155  
   156    EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1));
   157    EXPECT_EQ('h', bad_buffer_[0]);
   158  }
   159  
   160  TEST_F(PartialBadBufferTest, PreadvBig) {
   161    struct iovec vec;
   162    vec.iov_base = bad_buffer_;
   163    vec.iov_len = kPageSize;
   164  
   165    EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1));
   166    EXPECT_EQ('h', bad_buffer_[0]);
   167  }
   168  
   169  TEST_F(PartialBadBufferTest, PreadvSmall) {
   170    struct iovec vec;
   171    vec.iov_base = bad_buffer_;
   172    vec.iov_len = 10;
   173  
   174    EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1));
   175    EXPECT_EQ('h', bad_buffer_[0]);
   176  }
   177  
   178  TEST_F(PartialBadBufferTest, WriteBig) {
   179    off_t orig_size = Size();
   180    int n;
   181  
   182    ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds());
   183    EXPECT_THAT(
   184        (n = RetryEINTR(write)(fd_, bad_buffer_, kPageSize)),
   185        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   186    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   187  }
   188  
   189  TEST_F(PartialBadBufferTest, WriteSmall) {
   190    off_t orig_size = Size();
   191    int n;
   192  
   193    ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds());
   194    EXPECT_THAT(
   195        (n = RetryEINTR(write)(fd_, bad_buffer_, 10)),
   196        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   197    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   198  }
   199  
   200  TEST_F(PartialBadBufferTest, PwriteBig) {
   201    off_t orig_size = Size();
   202    int n;
   203  
   204    EXPECT_THAT(
   205        (n = RetryEINTR(pwrite)(fd_, bad_buffer_, kPageSize, orig_size)),
   206        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   207    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   208  }
   209  
   210  TEST_F(PartialBadBufferTest, PwriteSmall) {
   211    off_t orig_size = Size();
   212    int n;
   213  
   214    EXPECT_THAT(
   215        (n = RetryEINTR(pwrite)(fd_, bad_buffer_, 10, orig_size)),
   216        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   217    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   218  }
   219  
   220  TEST_F(PartialBadBufferTest, WritevBig) {
   221    struct iovec vec;
   222    vec.iov_base = bad_buffer_;
   223    vec.iov_len = kPageSize;
   224    off_t orig_size = Size();
   225    int n;
   226  
   227    ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds());
   228    EXPECT_THAT(
   229        (n = RetryEINTR(writev)(fd_, &vec, 1)),
   230        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   231    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   232  }
   233  
   234  TEST_F(PartialBadBufferTest, WritevSmall) {
   235    struct iovec vec;
   236    vec.iov_base = bad_buffer_;
   237    vec.iov_len = 10;
   238    off_t orig_size = Size();
   239    int n;
   240  
   241    ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds());
   242    EXPECT_THAT(
   243        (n = RetryEINTR(writev)(fd_, &vec, 1)),
   244        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   245    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   246  }
   247  
   248  TEST_F(PartialBadBufferTest, PwritevBig) {
   249    struct iovec vec;
   250    vec.iov_base = bad_buffer_;
   251    vec.iov_len = kPageSize;
   252    off_t orig_size = Size();
   253    int n;
   254  
   255    EXPECT_THAT(
   256        (n = RetryEINTR(pwritev)(fd_, &vec, 1, orig_size)),
   257        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   258    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   259  }
   260  
   261  TEST_F(PartialBadBufferTest, PwritevSmall) {
   262    struct iovec vec;
   263    vec.iov_base = bad_buffer_;
   264    vec.iov_len = 10;
   265    off_t orig_size = Size();
   266    int n;
   267  
   268    EXPECT_THAT(
   269        (n = RetryEINTR(pwritev)(fd_, &vec, 1, orig_size)),
   270        AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1)));
   271    EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0));
   272  }
   273  
   274  // getdents returns EFAULT when the you claim the buffer is large enough, but
   275  // it actually isn't.
   276  TEST_F(PartialBadBufferTest, GetdentsBig) {
   277    EXPECT_THAT(RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_,
   278                                    kPageSize),
   279                SyscallFailsWithErrno(EFAULT));
   280  }
   281  
   282  // getdents returns EINVAL when the you claim the buffer is too small.
   283  TEST_F(PartialBadBufferTest, GetdentsSmall) {
   284    EXPECT_THAT(
   285        RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_, 10),
   286        SyscallFailsWithErrno(EINVAL));
   287  }
   288  
   289  // getdents will write entries into a buffer if there is space before it faults.
   290  TEST_F(PartialBadBufferTest, GetdentsOneEntry) {
   291    // 30 bytes is enough for one (small) entry.
   292    char* buf = FreeBytes(30);
   293  
   294    EXPECT_THAT(
   295        RetryEINTR(syscall)(SYS_getdents64, directory_fd_, buf, kPageSize),
   296        SyscallSucceedsWithValue(Gt(0)));
   297  }
   298  
   299  PosixErrorOr<sockaddr_storage> InetLoopbackAddr(int family) {
   300    struct sockaddr_storage addr;
   301    memset(&addr, 0, sizeof(addr));
   302    addr.ss_family = family;
   303    switch (family) {
   304      case AF_INET:
   305        reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr =
   306            htonl(INADDR_LOOPBACK);
   307        break;
   308      case AF_INET6:
   309        reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr =
   310            in6addr_loopback;
   311        break;
   312      default:
   313        return PosixError(EINVAL,
   314                          absl::StrCat("unknown socket family: ", family));
   315    }
   316    return addr;
   317  }
   318  
   319  // SendMsgTCP verifies that calling sendmsg with a bad address returns an
   320  // EFAULT. It also verifies that passing a buffer which is made up of 2
   321  // pages one valid and one guard page succeeds as long as the write is
   322  // for exactly the size of 1 page.
   323  TEST_F(PartialBadBufferTest, SendMsgTCP) {
   324    // FIXME(b/171436815): Netstack save/restore is broken.
   325    const DisableSave ds;
   326  
   327    auto listen_socket =
   328        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
   329  
   330    // Initialize address to the loopback one.
   331    sockaddr_storage addr = ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(AF_INET));
   332    socklen_t addrlen = sizeof(addr);
   333  
   334    // Bind to some port then start listening.
   335    ASSERT_THAT(bind(listen_socket.get(),
   336                     reinterpret_cast<struct sockaddr*>(&addr), addrlen),
   337                SyscallSucceeds());
   338  
   339    ASSERT_THAT(listen(listen_socket.get(), SOMAXCONN), SyscallSucceeds());
   340  
   341    // Get the address we're listening on, then connect to it. We need to do this
   342    // because we're allowing the stack to pick a port for us.
   343    ASSERT_THAT(getsockname(listen_socket.get(),
   344                            reinterpret_cast<struct sockaddr*>(&addr), &addrlen),
   345                SyscallSucceeds());
   346  
   347    auto send_socket =
   348        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
   349  
   350    ASSERT_THAT(
   351        RetryEINTR(connect)(send_socket.get(),
   352                            reinterpret_cast<struct sockaddr*>(&addr), addrlen),
   353        SyscallSucceeds());
   354  
   355    // Accept the connection.
   356    auto recv_socket =
   357        ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_socket.get(), nullptr, nullptr));
   358  
   359    // TODO(gvisor.dev/issue/674): Update this once Netstack matches linux
   360    //   behaviour on a setsockopt of SO_RCVBUF/SO_SNDBUF.
   361    //
   362    // Set SO_SNDBUF for socket to exactly kPageSize+1.
   363    //
   364    // gVisor does not double the value passed in SO_SNDBUF like linux does so we
   365    // just increase it by 1 byte here for gVisor so that we can test writing 1
   366    // byte past the valid page and check that it triggers an EFAULT
   367    // correctly. Otherwise in gVisor the sendmsg call will just return with no
   368    // error with kPageSize bytes written successfully.
   369    const uint32_t buf_size = kPageSize + 1;
   370    ASSERT_THAT(setsockopt(send_socket.get(), SOL_SOCKET, SO_SNDBUF, &buf_size,
   371                           sizeof(buf_size)),
   372                SyscallSucceedsWithValue(0));
   373  
   374    struct msghdr hdr = {};
   375    struct iovec iov = {};
   376    iov.iov_base = bad_buffer_;
   377    iov.iov_len = kPageSize;
   378    hdr.msg_iov = &iov;
   379    hdr.msg_iovlen = 1;
   380  
   381    ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0),
   382                SyscallFailsWithErrno(EFAULT));
   383  
   384    // Now assert that writing kPageSize from addr_ succeeds.
   385    iov.iov_base = addr_;
   386    ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0),
   387                SyscallSucceedsWithValue(kPageSize));
   388    // Read all the data out so that we drain the socket SND_BUF on the sender.
   389    std::vector<char> buffer(kPageSize);
   390    ASSERT_THAT(RetryEINTR(read)(recv_socket.get(), buffer.data(), kPageSize),
   391                SyscallSucceedsWithValue(kPageSize));
   392  
   393    // Sleep for a shortwhile to ensure that we have time to process the
   394    // ACKs. This is not strictly required unless running under gotsan which is a
   395    // lot slower and can result in the next write to write only 1 byte instead of
   396    // our intended kPageSize + 1.
   397    absl::SleepFor(absl::Milliseconds(50));
   398  
   399    // Now assert that writing > kPageSize results in EFAULT.
   400    iov.iov_len = kPageSize + 1;
   401    ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0),
   402                SyscallFailsWithErrno(EFAULT));
   403  }
   404  
   405  }  // namespace
   406  
   407  }  // namespace testing
   408  }  // namespace gvisor