gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/exit.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 <sys/wait.h>
    16  #include <unistd.h>
    17  
    18  #include "gtest/gtest.h"
    19  #include "absl/flags/flag.h"
    20  #include "absl/time/time.h"
    21  #include "test/util/file_descriptor.h"
    22  #include "test/util/multiprocess_util.h"
    23  #include "test/util/test_util.h"
    24  #include "test/util/thread_util.h"
    25  #include "test/util/time_util.h"
    26  
    27  ABSL_FLAG(bool, test_child, false,
    28            "If true, run the ExitAllThreads child workload.");
    29  
    30  namespace gvisor {
    31  namespace testing {
    32  
    33  namespace {
    34  
    35  void TestExit(int code) {
    36    pid_t pid = fork();
    37    if (pid == 0) {
    38      _exit(code);
    39    }
    40  
    41    ASSERT_THAT(pid, SyscallSucceeds());
    42  
    43    int status;
    44    EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds());
    45    EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == code) << status;
    46  }
    47  
    48  TEST(ExitTest, Success) { TestExit(0); }
    49  
    50  TEST(ExitTest, Failure) { TestExit(1); }
    51  
    52  // This test ensures that a process's file descriptors are closed when it calls
    53  // exit(). In order to test this, the parent tries to read from a pipe whose
    54  // write end is held by the child. While the read is blocking, the child exits,
    55  // which should cause the parent to read 0 bytes due to EOF.
    56  TEST(ExitTest, CloseFds) {
    57    int pipe_fds[2];
    58    ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds());
    59  
    60    FileDescriptor read_fd(pipe_fds[0]);
    61    FileDescriptor write_fd(pipe_fds[1]);
    62  
    63    pid_t pid = fork();
    64    if (pid == 0) {
    65      read_fd.reset();
    66  
    67      SleepSafe(absl::Seconds(10));
    68  
    69      _exit(0);
    70    }
    71  
    72    EXPECT_THAT(pid, SyscallSucceeds());
    73  
    74    write_fd.reset();
    75  
    76    char buf[10];
    77    EXPECT_THAT(ReadFd(read_fd.get(), buf, sizeof(buf)),
    78                SyscallSucceedsWithValue(0));
    79  }
    80  
    81  TEST(ExitTest, ExitAllThreads) {
    82    pid_t child_pid = -1;
    83    int execve_errno = 0;
    84    auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(
    85        ForkAndExec("/proc/self/exe", {"/proc/self/exe", "--test_child"}, {},
    86                    nullptr, &child_pid, &execve_errno));
    87    ASSERT_GT(child_pid, 0);
    88    ASSERT_EQ(execve_errno, 0);
    89  
    90    int status;
    91    EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds());
    92    EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status;
    93  }
    94  
    95  void RunChild() {
    96    ScopedThread t([] { _exit(0); });
    97    t.Join();
    98    // Should not be reached
    99    abort();
   100  }
   101  
   102  // SIGKILL of zombied thread group does not change exit status.
   103  TEST(ExitTest, SigkillZombieGroup) {
   104    int pipe_fds[2];
   105    ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds());
   106  
   107    FileDescriptor read_fd(pipe_fds[0]);
   108    FileDescriptor write_fd(pipe_fds[1]);
   109  
   110    pid_t pid = fork();
   111    if (pid == 0) {
   112      read_fd.reset();
   113  
   114      _exit(0);
   115    }
   116  
   117    EXPECT_THAT(pid, SyscallSucceeds());
   118    write_fd.reset();
   119  
   120    // Wait for pipe to automatically close to indicate that the child is zombied.
   121    char buf[10];
   122    EXPECT_THAT(ReadFd(read_fd.get(), buf, sizeof(buf)),
   123                SyscallSucceedsWithValue(0));
   124  
   125    EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds());
   126  
   127    // SIGKILL did not change exit status.
   128    int status;
   129    EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds());
   130    EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status;
   131  }
   132  
   133  // Variant of SigkillZombieGroup using exit(2) instead of exit_group(2).
   134  TEST(ExitTest, SigkillZombieThread) {
   135    int pipe_fds[2];
   136    ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds());
   137  
   138    FileDescriptor read_fd(pipe_fds[0]);
   139    FileDescriptor write_fd(pipe_fds[1]);
   140  
   141    pid_t pid = fork();
   142    if (pid == 0) {
   143      read_fd.reset();
   144  
   145      syscall(SYS_exit, 0);
   146    }
   147  
   148    EXPECT_THAT(pid, SyscallSucceeds());
   149    write_fd.reset();
   150  
   151    // Wait for pipe to automatically close to indicate that the child is zombied.
   152    char buf[10];
   153    EXPECT_THAT(ReadFd(read_fd.get(), buf, sizeof(buf)),
   154                SyscallSucceedsWithValue(0));
   155  
   156    EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds());
   157  
   158    // SIGKILL did not change exit status.
   159    int status;
   160    EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds());
   161    EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status;
   162  }
   163  
   164  }  // namespace
   165  
   166  }  // namespace testing
   167  }  // namespace gvisor
   168  
   169  int main(int argc, char** argv) {
   170    gvisor::testing::TestInit(&argc, &argv);
   171  
   172    if (absl::GetFlag(FLAGS_test_child)) {
   173      gvisor::testing::RunChild();
   174      return 1;
   175    }
   176  
   177    return gvisor::testing::RunAllTests();
   178  }