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 }