gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/multiprocess_util.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/util/multiprocess_util.h"
    16  
    17  #include <asm/unistd.h>
    18  #include <errno.h>
    19  #include <fcntl.h>
    20  #include <signal.h>
    21  #include <sys/prctl.h>
    22  #include <sys/socket.h>
    23  #include <unistd.h>
    24  
    25  #include <functional>
    26  #include <string>
    27  
    28  #include "gtest/gtest.h"
    29  #include "absl/strings/str_cat.h"
    30  #include "absl/strings/str_format.h"
    31  #include "test/util/cleanup.h"
    32  #include "test/util/file_descriptor.h"
    33  #include "test/util/logging.h"
    34  #include "test/util/posix_error.h"
    35  #include "test/util/save_util.h"
    36  #include "test/util/test_util.h"
    37  
    38  namespace gvisor {
    39  namespace testing {
    40  
    41  namespace {
    42  
    43  // exec_fn wraps a variant of the exec family, e.g. execve or execveat.
    44  PosixErrorOr<Cleanup> ForkAndExecHelper(const std::function<void()>& exec_fn,
    45                                          const std::function<void()>& fn,
    46                                          pid_t* child, int* execve_errno) {
    47    int pfds[2];
    48    int ret = pipe2(pfds, O_CLOEXEC);
    49    if (ret < 0) {
    50      return PosixError(errno, "pipe failed");
    51    }
    52    FileDescriptor rfd(pfds[0]);
    53    FileDescriptor wfd(pfds[1]);
    54  
    55    int parent_stdout = dup(STDOUT_FILENO);
    56    if (parent_stdout < 0) {
    57      return PosixError(errno, "dup stdout");
    58    }
    59    int parent_stderr = dup(STDERR_FILENO);
    60    if (parent_stdout < 0) {
    61      return PosixError(errno, "dup stderr");
    62    }
    63  
    64    pid_t pid = fork();
    65    if (pid < 0) {
    66      return PosixError(errno, "fork failed");
    67    } else if (pid == 0) {
    68      // Child.
    69      rfd.reset();
    70      if (dup2(parent_stdout, STDOUT_FILENO) < 0) {
    71        _exit(3);
    72      }
    73      if (dup2(parent_stderr, STDERR_FILENO) < 0) {
    74        _exit(4);
    75      }
    76      close(parent_stdout);
    77      close(parent_stderr);
    78  
    79      // Clean ourself up in case the parent doesn't.
    80      if (prctl(PR_SET_PDEATHSIG, SIGKILL)) {
    81        _exit(3);
    82      }
    83  
    84      if (fn) {
    85        fn();
    86      }
    87  
    88      // Call variant of exec function.
    89      exec_fn();
    90  
    91      int error = errno;
    92      if (WriteFd(pfds[1], &error, sizeof(error)) != sizeof(error)) {
    93        // We can't do much if the write fails, but we can at least exit with a
    94        // different code.
    95        _exit(2);
    96      }
    97      _exit(1);
    98    }
    99  
   100    // Parent.
   101    if (child) {
   102      *child = pid;
   103    }
   104  
   105    auto cleanup = Cleanup([pid] {
   106      kill(pid, SIGKILL);
   107      RetryEINTR(waitpid)(pid, nullptr, 0);
   108    });
   109  
   110    wfd.reset();
   111  
   112    int read_errno;
   113    ret = ReadFd(rfd.get(), &read_errno, sizeof(read_errno));
   114    if (ret == 0) {
   115      // Other end of the pipe closed, execve must have succeeded.
   116      read_errno = 0;
   117    } else if (ret < 0) {
   118      return PosixError(errno, "read pipe failed");
   119    } else if (ret != sizeof(read_errno)) {
   120      return PosixError(EPIPE, absl::StrCat("pipe read wrong size ", ret));
   121    }
   122  
   123    if (execve_errno) {
   124      *execve_errno = read_errno;
   125    }
   126  
   127    return std::move(cleanup);
   128  }
   129  
   130  }  // namespace
   131  
   132  PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
   133                                    const ExecveArray& argv,
   134                                    const ExecveArray& envv,
   135                                    const std::function<void()>& fn, pid_t* child,
   136                                    int* execve_errno) {
   137    char* const* argv_data = argv.get();
   138    char* const* envv_data = envv.get();
   139    const std::function<void()> exec_fn = [=] {
   140      execve(filename.c_str(), argv_data, envv_data);
   141    };
   142    return ForkAndExecHelper(exec_fn, fn, child, execve_errno);
   143  }
   144  
   145  PosixErrorOr<Cleanup> ForkAndExecveat(const int32_t dirfd,
   146                                        const std::string& pathname,
   147                                        const ExecveArray& argv,
   148                                        const ExecveArray& envv, const int flags,
   149                                        const std::function<void()>& fn,
   150                                        pid_t* child, int* execve_errno) {
   151    char* const* argv_data = argv.get();
   152    char* const* envv_data = envv.get();
   153    const std::function<void()> exec_fn = [=] {
   154      syscall(__NR_execveat, dirfd, pathname.c_str(), argv_data, envv_data,
   155              flags);
   156    };
   157    return ForkAndExecHelper(exec_fn, fn, child, execve_errno);
   158  }
   159  
   160  PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn) {
   161    pid_t pid = fork();
   162    if (pid == 0) {
   163      fn();
   164      TEST_CHECK_MSG(!::testing::Test::HasFailure(),
   165                     "EXPECT*/ASSERT* failed. These are not async-signal-safe "
   166                     "and must not be called from fn.");
   167      _exit(0);
   168    }
   169    MaybeSave();
   170    if (pid < 0) {
   171      return PosixError(errno, "fork failed");
   172    }
   173  
   174    int status;
   175    if (waitpid(pid, &status, 0) < 0) {
   176      return PosixError(errno, "waitpid failed");
   177    }
   178  
   179    return status;
   180  }
   181  
   182  PosixErrorOr<int> InForkedUserMountNamespace(
   183      const std::function<void()>& parent, const std::function<void()>& child) {
   184    std::string umap_str = absl::StrFormat("0 %lu 1", geteuid());
   185    std::string gmap_str = absl::StrFormat("0 %lu 1", getegid());
   186    int sync_sks[2] = {};
   187    TEST_CHECK_SUCCESS(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sync_sks));
   188  
   189    pid_t pid = fork();
   190    if (pid == 0) {
   191      TEST_CHECK_SUCCESS(close(sync_sks[0]));
   192      TEST_CHECK(unshare(CLONE_NEWNS | CLONE_NEWUSER) == 0);
   193  
   194      // Setup uid and gid maps for child.
   195      int fd = open("/proc/self/uid_map", O_WRONLY);
   196      TEST_CHECK(fd > 0);
   197      TEST_CHECK(write(fd, umap_str.c_str(), umap_str.size()) > 0);
   198      TEST_CHECK(close(fd) == 0);
   199  
   200      // setgroups isn't implemented in gVisor but is necessary for native
   201      // tests.
   202      fd = open("/proc/self/setgroups", O_WRONLY);
   203      if (fd > 0) {
   204        TEST_CHECK(write(fd, "deny", 4) > 0);
   205        TEST_CHECK(close(fd) == 0);
   206      }
   207  
   208      fd = open("/proc/self/gid_map", O_WRONLY);
   209      TEST_CHECK(fd > 0);
   210      TEST_CHECK(write(fd, gmap_str.c_str(), gmap_str.size()) > 0);
   211      TEST_CHECK(close(fd) == 0);
   212  
   213      // Wait until uid and gid maps are setup.
   214      TEST_CHECK(setuid(0) == 0);
   215      TEST_CHECK(setgid(0) == 0);
   216  
   217      // Mount/user namespace setup is complete. Now run the parent function.
   218      TEST_CHECK_SUCCESS(shutdown(sync_sks[1], SHUT_WR));
   219      char s;
   220      // Wait for the parent function to be complete.
   221      TEST_CHECK(read(sync_sks[1], &s, 1) == 0);
   222      TEST_CHECK_SUCCESS(close(sync_sks[1]));
   223      // Parent function is complete. Now run the child function.
   224      child();
   225      TEST_CHECK_MSG(!::testing::Test::HasFailure(),
   226                     "EXPECT*/ASSERT* failed. These are not async-signal-safe "
   227                     "and must not be called from fn.");
   228      _exit(0);
   229    }
   230    MaybeSave();
   231    if (pid < 0) {
   232      return PosixError(errno, "fork failed");
   233    }
   234  
   235    close(sync_sks[1]);
   236    char s;
   237    // Wait for mount/user namespace setup to be complete.
   238    TEST_CHECK_SUCCESS(read(sync_sks[0], &s, 1));
   239    parent();
   240    // Now start the child function.
   241    TEST_CHECK_SUCCESS(close(sync_sks[0]));
   242  
   243    int status;
   244    if (waitpid(pid, &status, 0) < 0) {
   245      return PosixError(errno, "waitpid failed");
   246    }
   247    return status;
   248  }
   249  
   250  }  // namespace testing
   251  }  // namespace gvisor