gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/vfork.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 <sys/types.h>
    17  #include <sys/wait.h>
    18  #include <unistd.h>
    19  
    20  #include <string>
    21  #include <utility>
    22  
    23  #include "gmock/gmock.h"
    24  #include "gtest/gtest.h"
    25  #include "absl/flags/flag.h"
    26  #include "absl/time/time.h"
    27  #include "test/util/logging.h"
    28  #include "test/util/multiprocess_util.h"
    29  #include "test/util/test_util.h"
    30  #include "test/util/time_util.h"
    31  
    32  ABSL_FLAG(bool, vfork_test_child, false,
    33            "If true, run the VforkTest child workload.");
    34  
    35  namespace gvisor {
    36  namespace testing {
    37  
    38  namespace {
    39  
    40  // We don't test with raw CLONE_VFORK to avoid interacting with glibc's use of
    41  // TLS.
    42  //
    43  // Even with vfork(2), we must be careful to do little more in the child than
    44  // call execve(2). We use the simplest sleep function possible, though this is
    45  // still precarious, as we're officially only allowed to call execve(2) and
    46  // _exit(2).
    47  constexpr absl::Duration kChildDelay = absl::Seconds(10);
    48  
    49  // Exit code for successful child subprocesses. We don't want to use 0 since
    50  // it's too common, and an execve(2) failure causes the child to exit with the
    51  // errno, so kChildExitCode is chosen to be an unlikely errno:
    52  constexpr int kChildExitCode = 118;  // ENOTNAM: Not a XENIX named type file
    53  
    54  int64_t MonotonicNow() {
    55    struct timespec now;
    56    TEST_PCHECK(clock_gettime(CLOCK_MONOTONIC, &now) == 0);
    57    return now.tv_sec * 1000000000ll + now.tv_nsec;
    58  }
    59  
    60  TEST(VforkTest, ParentStopsUntilChildExits) {
    61    const auto test = [] {
    62      // N.B. Run the test in a single-threaded subprocess because
    63      // vfork is not safe in a multi-threaded process.
    64  
    65      const int64_t start = MonotonicNow();
    66  
    67      pid_t pid = vfork();
    68      if (pid == 0) {
    69        SleepSafe(kChildDelay);
    70        _exit(kChildExitCode);
    71      }
    72      TEST_PCHECK_MSG(pid > 0, "vfork failed");
    73      MaybeSave();
    74  
    75      const int64_t end = MonotonicNow();
    76  
    77      absl::Duration dur = absl::Nanoseconds(end - start);
    78  
    79      TEST_CHECK(dur >= kChildDelay);
    80  
    81      int status = 0;
    82      TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0));
    83      TEST_CHECK(WIFEXITED(status));
    84      TEST_CHECK(WEXITSTATUS(status) == kChildExitCode);
    85    };
    86  
    87    EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0));
    88  }
    89  
    90  TEST(VforkTest, ParentStopsUntilChildExecves) {
    91    ExecveArray const owned_child_argv = {"/proc/self/exe", "--vfork_test_child"};
    92    char* const* const child_argv = owned_child_argv.get();
    93  
    94    const auto test = [&] {
    95      const int64_t start = MonotonicNow();
    96  
    97      pid_t pid = vfork();
    98      if (pid == 0) {
    99        SleepSafe(kChildDelay);
   100        execve(child_argv[0], child_argv, /* envp = */ nullptr);
   101        _exit(errno);
   102      }
   103      // Don't attempt save/restore until after recording end_time,
   104      // since the test expects an upper bound on the time spent
   105      // stopped.
   106      int saved_errno = errno;
   107      const int64_t end = MonotonicNow();
   108      errno = saved_errno;
   109      TEST_PCHECK_MSG(pid > 0, "vfork failed");
   110      MaybeSave();
   111  
   112      absl::Duration dur = absl::Nanoseconds(end - start);
   113  
   114      // The parent should resume execution after execve, but before
   115      // the post-execve test child exits.
   116      TEST_CHECK(dur >= kChildDelay);
   117      TEST_CHECK(dur <= 2 * kChildDelay);
   118  
   119      int status = 0;
   120      TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0));
   121      TEST_CHECK(WIFEXITED(status));
   122      TEST_CHECK(WEXITSTATUS(status) == kChildExitCode);
   123    };
   124  
   125    EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0));
   126  }
   127  
   128  // A vfork child does not unstop the parent a second time when it exits after
   129  // exec.
   130  TEST(VforkTest, ExecedChildExitDoesntUnstopParent) {
   131    ExecveArray const owned_child_argv = {"/proc/self/exe", "--vfork_test_child"};
   132    char* const* const child_argv = owned_child_argv.get();
   133  
   134    const auto test = [&] {
   135      pid_t pid1 = vfork();
   136      if (pid1 == 0) {
   137        execve(child_argv[0], child_argv, /* envp = */ nullptr);
   138        _exit(errno);
   139      }
   140      TEST_PCHECK_MSG(pid1 > 0, "vfork failed");
   141      MaybeSave();
   142  
   143      // pid1 exec'd and is now sleeping.
   144      SleepSafe(kChildDelay / 2);
   145  
   146      const int64_t start = MonotonicNow();
   147  
   148      pid_t pid2 = vfork();
   149      if (pid2 == 0) {
   150        SleepSafe(kChildDelay);
   151        _exit(kChildExitCode);
   152      }
   153      TEST_PCHECK_MSG(pid2 > 0, "vfork failed");
   154      MaybeSave();
   155  
   156      const int64_t end = MonotonicNow();
   157  
   158      absl::Duration dur = absl::Nanoseconds(end - start);
   159  
   160      // The parent should resume execution only after pid2 exits, not
   161      // when pid1 exits.
   162      TEST_CHECK(dur >= kChildDelay);
   163  
   164      int status = 0;
   165      TEST_PCHECK(RetryEINTR(waitpid)(pid1, &status, 0));
   166      TEST_CHECK(WIFEXITED(status));
   167      TEST_CHECK(WEXITSTATUS(status) == kChildExitCode);
   168  
   169      TEST_PCHECK(RetryEINTR(waitpid)(pid2, &status, 0));
   170      TEST_CHECK(WIFEXITED(status));
   171      TEST_CHECK(WEXITSTATUS(status) == kChildExitCode);
   172    };
   173  
   174    EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0));
   175  }
   176  
   177  int RunChild() {
   178    SleepSafe(kChildDelay);
   179    return kChildExitCode;
   180  }
   181  
   182  }  // namespace
   183  
   184  }  // namespace testing
   185  }  // namespace gvisor
   186  
   187  int main(int argc, char** argv) {
   188    gvisor::testing::TestInit(&argc, &argv);
   189  
   190    if (absl::GetFlag(FLAGS_vfork_test_child)) {
   191      return gvisor::testing::RunChild();
   192    }
   193  
   194    return gvisor::testing::RunAllTests();
   195  }