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

     1  // Copyright 2022 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 <asm-generic/errno-base.h>
    16  #include <bits/types/siginfo_t.h>
    17  #include <bits/types/struct_iovec.h>
    18  #include <errno.h>
    19  #include <linux/futex.h>
    20  #include <string.h>
    21  #include <sys/types.h>
    22  #include <sys/uio.h>
    23  #include <sys/wait.h>
    24  #include <unistd.h>
    25  
    26  #include <algorithm>
    27  #include <atomic>
    28  #include <climits>
    29  #include <csignal>
    30  #include <cstddef>
    31  #include <cstdint>
    32  #include <functional>
    33  #include <iostream>
    34  #include <memory>
    35  #include <ostream>
    36  #include <string>
    37  #include <vector>
    38  
    39  #include "gmock/gmock.h"
    40  #include "gtest/gtest.h"
    41  #include "absl/cleanup/cleanup.h"
    42  #include "absl/strings/str_cat.h"
    43  #include "absl/strings/str_join.h"
    44  #include "test/util/cleanup.h"
    45  #include "test/util/logging.h"
    46  #include "test/util/memory_util.h"
    47  #include "test/util/posix_error.h"
    48  #include "test/util/test_util.h"
    49  #include "test/util/thread_util.h"
    50  
    51  namespace gvisor {
    52  namespace testing {
    53  
    54  namespace {
    55  
    56  class TestIovecs {
    57   public:
    58    TestIovecs(std::vector<std::string> data) {
    59      data_.resize(data.size());
    60      iovecs_.resize(data.size());
    61      for (size_t i = 0; i < data.size(); ++i) {
    62        data_[i] = data[i];
    63        bytes_ += data[i].size();
    64        iovecs_[i].iov_len = data_[i].size();
    65        iovecs_[i].iov_base = data_[i].data();
    66      }
    67    }
    68  
    69    void erase() {
    70      for (size_t i = 0; i < data_.size(); i++) {
    71        data_[i].clear();
    72      }
    73    }
    74  
    75    // Backing data that will be read/written.
    76    std::vector<std::string> data_;
    77  
    78    // Total size of data_.
    79    ssize_t bytes_ = 0;
    80  
    81    // Iovec structs that point into data_
    82    std::vector<iovec> iovecs_;
    83  };
    84  
    85  // bytes_match checks that the two TestIovecs are at least min_bytes in length,
    86  // and that they agree in the first min_bytes.
    87  bool bytes_match(TestIovecs first_iov, TestIovecs second_iov,
    88                   size_t min_bytes) {
    89    auto first = absl::StrJoin(first_iov.data_, "");
    90    if (first.size() < min_bytes) {
    91      std::cout << "First buffer smaller than min_bytes: " << min_bytes
    92                << " buffer: " << first << std::endl;
    93      return false;
    94    }
    95    first = first.substr(0, min_bytes);
    96  
    97    auto second = absl::StrJoin(second_iov.data_, "");
    98    if (second.size() < min_bytes) {
    99      std::cout << "First buffer smaller than min_bytes: " << min_bytes
   100                << " buffer: " << second << std::endl;
   101      return false;
   102    }
   103    second = second.substr(0, min_bytes);
   104  
   105    if (first != second) {
   106      std::cout << "Mismatch buffers:\n first: " << first
   107                << "\n second: " << second << std::endl;
   108      return false;
   109    }
   110  
   111    return true;
   112  }
   113  
   114  struct ProcessVMTestCase {
   115    std::string test_name;
   116    std::vector<std::string> local_data;
   117    std::vector<std::string> remote_data;
   118  };
   119  
   120  using ProcessVMTest = ::testing::TestWithParam<ProcessVMTestCase>;
   121  
   122  std::string getTestBuffer(std::string pattern, size_t size) {
   123    std::string s;
   124  
   125    auto pattern_length = pattern.length();
   126    s.reserve(size);
   127    while (s.length() + pattern_length < size) {
   128      s += pattern;
   129    }
   130    s += pattern.substr(0, size - s.length());
   131    return s;
   132  }
   133  
   134  INSTANTIATE_TEST_SUITE_P(
   135      ProcessVMTests, ProcessVMTest,
   136      ::testing::ValuesIn<ProcessVMTestCase>(
   137          {{"BothEmpty" /*test name*/,
   138            {""} /*local buffer*/,
   139            {""} /*remote buffer*/},
   140           {"EmptyLocal", {""}, {"All too easy."}},
   141           {"EmptyRemote", {"Impressive. Most impressive."}, {""}},
   142           {"SingleChar", {"l"}, {"r"}},
   143           {"LargerRemoteBuffer",
   144            {"OK, I'll try"},
   145            {"No!", "Try not", "Do...or do not", "There is no try."}},
   146           {"LargerLocalBuffer",
   147            {"Look!", "The cave is collapsing!"},
   148            {"This is no cave."}},
   149           {"BothWithMultipleIovecs",
   150            {"Obi-wan never told you what happened to your father.",
   151             "He told me enough...he told me you killed him."},
   152            {"No...I am your father.", "No. No.", "That's not true.",
   153             "That's impossible!"}},
   154           {
   155               "LargeBuffer",
   156               {
   157                   getTestBuffer(
   158                       "Train yourself to let go of everything you fear to lose.",
   159                       32 << 20),
   160                   "Hello there!",
   161               },
   162               {
   163                   "Do. Or do not. There is no try.",
   164                   getTestBuffer("The greatest teacher, failure is.", 32 << 20),
   165               },
   166           }}),
   167      [](const ::testing::TestParamInfo<ProcessVMTest::ParamType>& info) {
   168        return info.param.test_name;
   169      });
   170  
   171  // TestReadvSameProcess calls process_vm_readv in the same process with various
   172  // local/remote buffers.
   173  TEST_P(ProcessVMTest, TestReadvSameProcess) {
   174    TestIovecs local(GetParam().local_data);
   175    TestIovecs remote(GetParam().remote_data);
   176  
   177    auto want_size = std::min(remote.bytes_, local.bytes_);
   178    EXPECT_THAT(
   179        process_vm_readv(getpid(), local.iovecs_.data(), local.iovecs_.size(),
   180                         remote.iovecs_.data(), remote.iovecs_.size(), 0),
   181        SyscallSucceedsWithValue(want_size));
   182    EXPECT_TRUE(bytes_match(local, remote, want_size));
   183  }
   184  
   185  // TestReadvSameProcessDifferentThread calls process_vm_readv in the same
   186  // process, but with a different (non-leader) remote thread, with various
   187  // local/remote buffers.
   188  TEST_P(ProcessVMTest, TestReadvSameProcessDifferentThread) {
   189    TestIovecs local(GetParam().local_data);
   190    TestIovecs remote(GetParam().remote_data);
   191  
   192    std::atomic<std::int32_t> sibling_tid{0};
   193    std::atomic<std::uint32_t> sibling_exit{0};
   194    ScopedThread t([&] {
   195      sibling_tid.store(gettid());
   196      syscall(SYS_futex, &sibling_tid, FUTEX_WAKE, 1);
   197      while (sibling_exit.load() == 0) {
   198        syscall(SYS_futex, &sibling_exit, FUTEX_WAIT, 0, nullptr);
   199      }
   200    });
   201    Cleanup cleanup_t([&] {
   202      sibling_exit.store(1);
   203      syscall(SYS_futex, &sibling_exit, FUTEX_WAKE, 1);
   204    });
   205    while (sibling_tid.load() == 0) {
   206      syscall(SYS_futex, &sibling_tid, FUTEX_WAIT, 0, nullptr);
   207    }
   208  
   209    auto want_size = std::min(remote.bytes_, local.bytes_);
   210    EXPECT_THAT(process_vm_readv(sibling_tid.load(), local.iovecs_.data(),
   211                                 local.iovecs_.size(), remote.iovecs_.data(),
   212                                 remote.iovecs_.size(), 0),
   213                SyscallSucceedsWithValue(want_size));
   214    EXPECT_TRUE(bytes_match(local, remote, want_size));
   215  }
   216  
   217  // TestReadvSubProcess reads data from a forked child process.
   218  TEST_P(ProcessVMTest, TestReadvSubProcess) {
   219    TestIovecs local = TestIovecs(GetParam().local_data);
   220    TestIovecs remote = TestIovecs(GetParam().remote_data);
   221  
   222    pid_t pid = fork();
   223    TEST_CHECK_SUCCESS(pid);
   224    if (pid == 0) {
   225      // Child. This is the "remote" process.
   226      // Wait for parent to read data.
   227      sleep(10);  // NOLINT - SleepFor is not async-signal-safe.
   228      _exit(0);
   229    }
   230  
   231    auto cleanup = absl::MakeCleanup([&] { kill(pid, SIGKILL); });
   232  
   233    // Erase the string data in parent's copy of remote, to make sure we are
   234    // reading data from the child.
   235    remote.erase();
   236  
   237    // Compare against actual remote (not what we sent to the child, since we
   238    // emptied it).
   239    TestIovecs want_remote = TestIovecs(GetParam().remote_data);
   240    auto want_size = std::min(local.bytes_, want_remote.bytes_);
   241    ASSERT_THAT(process_vm_readv(pid, local.iovecs_.data(), local.iovecs_.size(),
   242                                 remote.iovecs_.data(), remote.iovecs_.size(), 0),
   243                SyscallSucceedsWithValue(want_size));
   244    EXPECT_TRUE(bytes_match(local, want_remote, want_size));
   245  }
   246  
   247  // TestWritevSameProcess calls process_vm_readv in the same process with various
   248  // local/remote buffers.
   249  TEST_P(ProcessVMTest, TestWritevSameProcess) {
   250    TestIovecs local(GetParam().local_data);
   251    TestIovecs remote(GetParam().remote_data);
   252  
   253    auto want_size = std::min(remote.bytes_, local.bytes_);
   254    EXPECT_THAT(
   255        process_vm_writev(getpid(), local.iovecs_.data(), local.iovecs_.size(),
   256                          remote.iovecs_.data(), remote.iovecs_.size(), 0),
   257        SyscallSucceedsWithValue(want_size));
   258    EXPECT_TRUE(bytes_match(local, remote, want_size));
   259  }
   260  
   261  // TestWritevSubProcess writes data to a forked child process.
   262  TEST_P(ProcessVMTest, TestWritevSubProcess) {
   263    TestIovecs local(GetParam().local_data);
   264    TestIovecs remote(GetParam().remote_data);
   265  
   266    // A pipe is used to wait on the write call from the parent, so we can block
   267    // asserting until the write is complete.
   268    int pipefd[2];
   269    ASSERT_THAT(pipe(pipefd), SyscallSucceeds());
   270  
   271    pid_t pid = fork();
   272    TEST_CHECK_SUCCESS(pid);
   273    if (pid == 0) {
   274      // Child. This is the "remote" process.
   275      close(pipefd[1]);
   276  
   277      // Wait on pipefd. It will be closed after the parent has written.
   278      char buf;
   279      TEST_CHECK_SUCCESS(read(pipefd[0], &buf, sizeof(buf)));
   280      close(pipefd[0]);
   281  
   282      // Check the data. This will exit non-0 in the case of a mismatch.
   283      auto want_size = std::min(local.bytes_, remote.bytes_);
   284      TEST_CHECK(bytes_match(local, remote, want_size));
   285  
   286      _exit(0);
   287    }
   288  
   289    auto cleanup = absl::MakeCleanup([&] {
   290      int status = 0;
   291      EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceeds());
   292      EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
   293    });
   294  
   295    // Write to the remote.
   296    auto want_size = std::min(local.bytes_, remote.bytes_);
   297    ASSERT_THAT(
   298        process_vm_writev(pid, local.iovecs_.data(), local.iovecs_.size(),
   299                          remote.iovecs_.data(), remote.iovecs_.size(), 0),
   300        SyscallSucceedsWithValue(want_size));
   301  
   302    // Now that we've written, close pipefd to signal the child can continue.
   303    close(pipefd[1]);
   304  }
   305  
   306  TEST(ProcessVMInvalidTest, NonZeroFlags) {
   307    struct iovec iov = {};
   308    // Flags should be 0.
   309    EXPECT_THAT(process_vm_readv(0, &iov, 1, &iov, 1, 10),
   310                SyscallFailsWithErrno(EINVAL));
   311    EXPECT_THAT(process_vm_writev(0, &iov, 1, &iov, 1, 10),
   312                SyscallFailsWithErrno(EINVAL));
   313  }
   314  
   315  TEST(ProcessVMInvalidTest, NullLocalIovec) {
   316    struct iovec iov = {};
   317    pid_t child = fork();
   318    if (child == 0) {
   319      sleep(10);  // NOLINT - SleepFor is not async-signal-safe.
   320      _exit(0);
   321    }
   322  
   323    EXPECT_THAT(process_vm_readv(child, nullptr, 1, &iov, 1, 0),
   324                SyscallFailsWithErrno(EFAULT));
   325  
   326    EXPECT_THAT(process_vm_writev(child, nullptr, 1, &iov, 1, 0),
   327                SyscallFailsWithErrno(EFAULT));
   328    EXPECT_THAT(kill(child, SIGKILL), SyscallSucceeds());
   329    EXPECT_THAT(waitpid(child, 0, 0), SyscallSucceeds());
   330  }
   331  
   332  TEST(ProcessVMInvalidTest, NULLRemoteIovec) {
   333    std::string contents = "3263827";
   334    struct iovec iov;
   335    iov.iov_base = contents.data();
   336    iov.iov_len = contents.size();
   337  
   338    pid_t pid = fork();
   339    TEST_CHECK_SUCCESS(pid);
   340    if (pid == 0) {
   341      sleep(10);  // NOLINT - SleepFor is not async-signal-safe.
   342      _exit(0);
   343    }
   344    auto cleanup = absl::MakeCleanup([&] { kill(pid, SIGKILL); });
   345  
   346    EXPECT_THAT(process_vm_readv(pid, &iov, contents.length(), nullptr, 1, 0),
   347                SyscallFailsWithErrno(::testing::AnyOf(EFAULT, EINVAL)));
   348    EXPECT_THAT(process_vm_writev(pid, &iov, contents.length(), nullptr, 1, 0),
   349                SyscallFailsWithErrno(::testing::AnyOf(EFAULT, EINVAL)));
   350  }
   351  
   352  TEST(ProcessVMInvalidTest, ProcessNoExist) {
   353    struct iovec iov;
   354    EXPECT_THAT(process_vm_readv(-1, &iov, 1, &iov, 1, 0),
   355                SyscallFailsWithErrno(::testing::AnyOf(ESRCH, EFAULT)));
   356    EXPECT_THAT(process_vm_writev(-1, &iov, 1, &iov, 1, 0),
   357                SyscallFailsWithErrno(::testing::AnyOf(ESRCH, EFAULT)));
   358  }
   359  
   360  TEST(ProcessVMInvalidTest, GreaterThanIOV_MAX) {
   361    std::string contents = "3263827";
   362    struct iovec iov;
   363    iov.iov_base = contents.data();
   364    iov.iov_len = contents.size();
   365  
   366    pid_t pid = fork();
   367    TEST_CHECK_SUCCESS(pid);
   368    if (pid == 0) {
   369      sleep(10);  // NOLINT - SleepFor is not async-signal-safe.
   370      _exit(0);
   371    }
   372    auto cleanup = absl::MakeCleanup([&] { kill(pid, SIGKILL); });
   373  
   374    EXPECT_THAT(process_vm_readv(pid, &iov, 1, &iov, IOV_MAX + 1, 0),
   375                SyscallFailsWithErrno(EINVAL));
   376    EXPECT_THAT(process_vm_writev(pid, &iov, 1, &iov, IOV_MAX + 1, 0),
   377                SyscallFailsWithErrno(EINVAL));
   378  }
   379  
   380  TEST(ProcessVMInvalidTest, PartialReadWrite) {
   381    std::string iov_content_1 = "1138";
   382    std::string iov_content_2 = "3720";
   383    struct iovec iov[2];
   384    iov[0].iov_base = iov_content_1.data();
   385    iov[0].iov_len = iov_content_1.size();
   386    iov[1].iov_base = iov_content_2.data();
   387    iov[1].iov_len = iov_content_2.size();
   388  
   389    std::string iov_corrupted_content_1 = iov_content_1;
   390    struct iovec corrupted_iov[2];
   391    corrupted_iov[0].iov_base = iov_corrupted_content_1.data();
   392    corrupted_iov[0].iov_len = iov_corrupted_content_1.size();
   393    corrupted_iov[1].iov_base = (void*)0xDEADBEEF;
   394    corrupted_iov[1].iov_len = 42;
   395  
   396    EXPECT_THAT(
   397        RetryEINTR(process_vm_writev)(getpid(), iov, 2, corrupted_iov, 2, 0),
   398        SyscallSucceedsWithValue(iov_content_1.size()));
   399    EXPECT_THAT(
   400        RetryEINTR(process_vm_readv)(getpid(), corrupted_iov, 2, iov, 2, 0),
   401        SyscallSucceedsWithValue(iov_content_1.size()));
   402    EXPECT_THAT(
   403        RetryEINTR(process_vm_writev)(getpid(), corrupted_iov, 2, iov, 2, 0),
   404        SyscallSucceedsWithValue(iov_content_1.size()));
   405    EXPECT_THAT(
   406        RetryEINTR(process_vm_readv)(getpid(), iov, 2, corrupted_iov, 2, 0),
   407        SyscallSucceedsWithValue(iov_content_1.size()));
   408  }
   409  
   410  TEST(ProcessVMInvalidTest, AccessInvalidMemoryFailsWithEINVAL) {
   411    auto const mapping =
   412        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE));
   413    struct iovec iov_none, iov_valid;
   414    char buf[128];
   415    iov_valid.iov_base = buf;
   416    iov_valid.iov_len = sizeof(buf);
   417    iov_none.iov_base = mapping.ptr();
   418    iov_none.iov_len = mapping.len();
   419  
   420    EXPECT_THAT(
   421        RetryEINTR(process_vm_writev)(getpid(), &iov_none, 1, &iov_valid, 1, 0),
   422        SyscallFailsWithErrno(EFAULT));
   423    EXPECT_THAT(
   424        RetryEINTR(process_vm_writev)(getpid(), &iov_valid, 1, &iov_none, 1, 0),
   425        SyscallFailsWithErrno(EFAULT));
   426  }
   427  
   428  TEST(ProcessVMTest, WriteToZombie) {
   429    char* data = {0};
   430    pid_t child;
   431    ASSERT_THAT(child = fork(), SyscallSucceeds());
   432    if (child == 0) {
   433      _exit(0);
   434    }
   435    siginfo_t siginfo = {};
   436    ASSERT_THAT(RetryEINTR(waitid)(P_PID, child, &siginfo, WEXITED | WNOWAIT),
   437                SyscallSucceeds());
   438    struct iovec iov;
   439    iov.iov_base = data;
   440    iov.iov_len = sizeof(data);
   441    ASSERT_THAT(process_vm_writev(child, &iov, 1, &iov, 1, 0),
   442                SyscallFailsWithErrno(ESRCH));
   443  }
   444  
   445  // TestReadvNull calls process_vm_readv with null iovecs and checks that they
   446  // succeed but return 0;
   447  TEST(ProcessVMTest, TestReadvNull) {
   448    TestIovecs local(std::vector<std::string>{"foo"});
   449    TestIovecs remote(std::vector<std::string>{"bar"});
   450  
   451    // Pass 0 for local.
   452    EXPECT_THAT(process_vm_readv(getpid(), 0, 0, remote.iovecs_.data(),
   453                                 remote.iovecs_.size(), 0),
   454                SyscallSucceedsWithValue(0));
   455  
   456    // Pass 0 for remote.
   457    EXPECT_THAT(process_vm_readv(getpid(), local.iovecs_.data(),
   458                                 local.iovecs_.size(), 0, 0, 0),
   459                SyscallSucceedsWithValue(0));
   460  }
   461  
   462  // TestWritevNull calls process_vm_writev with null iovecs and checks that they
   463  // succeed but return 0;
   464  TEST(ProcessVMTest, TestWritevNull) {
   465    TestIovecs local(std::vector<std::string>{"foo"});
   466    TestIovecs remote(std::vector<std::string>{"bar"});
   467  
   468    // Pass 0 for local.
   469    EXPECT_THAT(process_vm_writev(getpid(), 0, 0, remote.iovecs_.data(),
   470                                  remote.iovecs_.size(), 0),
   471                SyscallSucceedsWithValue(0));
   472  
   473    // Pass 0 for remote.
   474    EXPECT_THAT(process_vm_writev(getpid(), local.iovecs_.data(),
   475                                  local.iovecs_.size(), 0, 0, 0),
   476                SyscallSucceedsWithValue(0));
   477  }
   478  
   479  }  // namespace
   480  }  // namespace testing
   481  }  // namespace gvisor