// Copyright 2018 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "test/util/file_descriptor.h" #include "test/util/linux_capability_util.h" #include "test/util/logging.h" #include "test/util/memory_util.h" #include "test/util/posix_error.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" namespace gvisor { namespace testing { namespace { using ::testing::Ge; class ForkTest : public ::testing::Test { protected: // SetUp creates a populated, open file. void SetUp() override { // Make a shared mapping. shared_ = reinterpret_cast(mmap(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)); ASSERT_NE(reinterpret_cast(shared_), MAP_FAILED); // Make a private mapping. private_ = reinterpret_cast(mmap(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); ASSERT_NE(reinterpret_cast(private_), MAP_FAILED); // Make a pipe. ASSERT_THAT(pipe(pipes_), SyscallSucceeds()); } // TearDown frees associated resources. void TearDown() override { EXPECT_THAT(munmap(shared_, kPageSize), SyscallSucceeds()); EXPECT_THAT(munmap(private_, kPageSize), SyscallSucceeds()); EXPECT_THAT(close(pipes_[0]), SyscallSucceeds()); EXPECT_THAT(close(pipes_[1]), SyscallSucceeds()); } // Fork executes a clone system call. pid_t Fork() { pid_t pid = fork(); MaybeSave(); TEST_PCHECK_MSG(pid >= 0, "fork failed"); return pid; } // Wait waits for the given pid and returns the exit status. If the child was // killed by a signal or an error occurs, then 256+signal is returned. int Wait(pid_t pid) { int status; while (true) { int rval = wait4(pid, &status, 0, NULL); if (rval < 0) { return rval; } if (rval != pid) { continue; } if (WIFEXITED(status)) { return WEXITSTATUS(status); } if (WIFSIGNALED(status)) { return 256 + WTERMSIG(status); } } } // Exit exits the process. void Exit(int code) { _exit(code); // Should never reach here. Since the exit above failed, we really don't // have much in the way of options to indicate failure. So we just try to // log an assertion failure to the logs. The parent process will likely // fail anyways if exit is not working. TEST_CHECK_MSG(false, "_exit returned"); } // ReadByte reads a byte from the shared pipe. char ReadByte() { char val = -1; TEST_PCHECK(ReadFd(pipes_[0], &val, 1) == 1); MaybeSave(); return val; } // WriteByte writes a byte from the shared pipe. void WriteByte(char val) { TEST_PCHECK(WriteFd(pipes_[1], &val, 1) == 1); MaybeSave(); } // Shared pipe. int pipes_[2]; // Shared mapping (one page). char* shared_; // Private mapping (one page). char* private_; }; TEST_F(ForkTest, Simple) { pid_t child = Fork(); if (child == 0) { Exit(0); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } TEST_F(ForkTest, ExitCode) { pid_t child = Fork(); if (child == 0) { Exit(123); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(123)); child = Fork(); if (child == 0) { Exit(1); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(1)); } TEST_F(ForkTest, Multi) { pid_t child1 = Fork(); if (child1 == 0) { Exit(0); } pid_t child2 = Fork(); if (child2 == 0) { Exit(1); } EXPECT_THAT(Wait(child1), SyscallSucceedsWithValue(0)); EXPECT_THAT(Wait(child2), SyscallSucceedsWithValue(1)); } TEST_F(ForkTest, Pipe) { pid_t child = Fork(); if (child == 0) { WriteByte(1); Exit(0); } EXPECT_EQ(ReadByte(), 1); EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } TEST_F(ForkTest, SharedMapping) { pid_t child = Fork(); if (child == 0) { // Wait for the parent. ReadByte(); if (shared_[0] == 1) { Exit(0); } // Failed. Exit(1); } // Change the mapping. ASSERT_EQ(shared_[0], 0); shared_[0] = 1; // Unblock the child. WriteByte(0); // Did it work? EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } TEST_F(ForkTest, PrivateMapping) { pid_t child = Fork(); if (child == 0) { // Wait for the parent. ReadByte(); if (private_[0] == 0) { Exit(0); } // Failed. Exit(1); } // Change the mapping. ASSERT_EQ(private_[0], 0); private_[0] = 1; // Unblock the child. WriteByte(0); // Did it work? EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } // CPUID is x86 specific. #ifdef __x86_64__ // Test that cpuid works after a fork. TEST_F(ForkTest, Cpuid) { pid_t child = Fork(); // We should be able to determine the CPU vendor. ASSERT_NE(GetCPUVendor(), CPUVendor::kUnknownVendor); if (child == 0) { Exit(0); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } #endif TEST_F(ForkTest, Mmap) { pid_t child = Fork(); if (child == 0) { void* addr = mmap(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); MaybeSave(); Exit(addr == MAP_FAILED); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } static volatile int alarmed = 0; void AlarmHandler(int sig, siginfo_t* info, void* context) { alarmed = 1; } TEST_F(ForkTest, Alarm) { // Setup an alarm handler. struct sigaction sa; sa.sa_sigaction = AlarmHandler; sigfillset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; EXPECT_THAT(sigaction(SIGALRM, &sa, nullptr), SyscallSucceeds()); pid_t child = Fork(); if (child == 0) { alarm(1); sleep(3); if (!alarmed) { Exit(1); } Exit(0); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); EXPECT_EQ(0, alarmed); } // Child cannot affect parent private memory. Regression test for b/24137240. TEST_F(ForkTest, PrivateMemory) { std::atomic local(0); pid_t child1 = Fork(); if (child1 == 0) { local++; pid_t child2 = Fork(); if (child2 == 0) { local++; TEST_CHECK(local.load() == 2); Exit(0); } TEST_PCHECK(Wait(child2) == 0); TEST_CHECK(local.load() == 1); Exit(0); } EXPECT_THAT(Wait(child1), SyscallSucceedsWithValue(0)); EXPECT_EQ(0, local.load()); } // Kernel-accessed buffers should remain coherent across COW. // // The buffer must be >= usermem.ZeroCopyMinBytes, as UnsafeAccess operates // differently. Regression test for b/33811887. TEST_F(ForkTest, COWSegment) { constexpr int kBufSize = 1024; char* read_buf = private_; char* touch = private_ + kPageSize / 2; std::string contents(kBufSize, 'a'); ScopedThread t([&] { // Wait to be sure the parent is blocked in read. absl::SleepFor(absl::Seconds(3)); // Fork to mark private pages for COW. // // Use fork directly rather than the Fork wrapper to skip the multi-threaded // check, and limit the child to async-signal-safe functions: // // "After a fork() in a multithreaded program, the child can safely call // only async-signal-safe functions (see signal(7)) until such time as it // calls execve(2)." // // Skip ASSERT in the child, as it isn't async-signal-safe. pid_t child = fork(); if (child == 0) { // Wait to be sure parent touched memory. sleep(3); Exit(0); } // Check success only in the parent. ASSERT_THAT(child, SyscallSucceedsWithValue(Ge(0))); // Trigger COW on private page. *touch = 42; // Write to pipe. Parent should still be able to read this. EXPECT_THAT(WriteFd(pipes_[1], contents.c_str(), kBufSize), SyscallSucceedsWithValue(kBufSize)); EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); }); EXPECT_THAT(ReadFd(pipes_[0], read_buf, kBufSize), SyscallSucceedsWithValue(kBufSize)); EXPECT_STREQ(contents.c_str(), read_buf); } TEST_F(ForkTest, SigAltStack) { std::vector stack_mem(SIGSTKSZ); stack_t stack = {}; stack.ss_size = SIGSTKSZ; stack.ss_sp = stack_mem.data(); ASSERT_THAT(sigaltstack(&stack, nullptr), SyscallSucceeds()); pid_t child = Fork(); if (child == 0) { stack_t oss = {}; TEST_PCHECK(sigaltstack(nullptr, &oss) == 0); MaybeSave(); TEST_CHECK((oss.ss_flags & SS_DISABLE) == 0); TEST_CHECK(oss.ss_size == (size_t)SIGSTKSZ); TEST_CHECK(oss.ss_sp == stack.ss_sp); Exit(0); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } TEST_F(ForkTest, Affinity) { // sched_setaffinity is a no-op on platform/KVM. SKIP_IF(GvisorPlatform() == Platform::kKVM); // Make a non-default cpumask. cpu_set_t parent_mask; EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &parent_mask), SyscallSucceeds()); // Knock out the lowest bit. for (unsigned int n = 0; n < CPU_SETSIZE; n++) { if (CPU_ISSET(n, &parent_mask)) { CPU_CLR(n, &parent_mask); break; } } EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &parent_mask), SyscallSucceeds()); pid_t child = Fork(); if (child == 0) { cpu_set_t child_mask; int ret = sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &child_mask); MaybeSave(); if (ret < 0) { Exit(-ret); } TEST_CHECK(CPU_EQUAL(&child_mask, &parent_mask)); Exit(0); } EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); } TEST(CloneTest, NewUserNamespacePermitsAllOtherNamespaces) { // "If CLONE_NEWUSER is specified along with other CLONE_NEW* flags in a // single clone(2) or unshare(2) call, the user namespace is guaranteed to be // created first, giving the child (clone(2)) or caller (unshare(2)) // privileges over the remaining namespaces created by the call. Thus, it is // possible for an unprivileged caller to specify this combination of flags." // - user_namespaces(7) SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); Mapping child_stack = ASSERT_NO_ERRNO_AND_VALUE( MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); int child_pid; // We only test with CLONE_NEWIPC, CLONE_NEWNET, and CLONE_NEWUTS since these // namespaces were implemented in Linux before user namespaces. ASSERT_THAT( child_pid = clone( +[](void*) { return 0; }, reinterpret_cast(child_stack.addr() + kPageSize), CLONE_NEWUSER | CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWUTS | SIGCHLD, /* arg = */ nullptr), SyscallSucceeds()); int status; ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceedsWithValue(child_pid)); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << "status = " << status; } TEST(CloneTest, NewUserMountNamespace) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); Mapping child_stack = ASSERT_NO_ERRNO_AND_VALUE( MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); int child_pid; ASSERT_THAT(child_pid = clone( +[](void*) { TEST_CHECK_SUCCESS(mount(nullptr, "/", nullptr, MS_REC | MS_PRIVATE, nullptr)); return 0; }, reinterpret_cast(child_stack.addr() + kPageSize), CLONE_NEWUSER | CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWUTS | SIGCHLD | CLONE_NEWNS, /* arg = */ nullptr), SyscallSucceeds()); int status; ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceedsWithValue(child_pid)); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << "status = " << status; } // Clone with CLONE_SETTLS and a non-canonical TLS address is rejected. TEST(CloneTest, NonCanonicalTLS) { constexpr uintptr_t kNonCanonical = 1ull << 63; // We need a valid address for the stack pointer. We'll never actually execute // on this. char stack; // The raw system call interface on x86-64 is: // long clone(unsigned long flags, void *stack, // int *parent_tid, int *child_tid, // unsigned long tls); // // While on arm64, the order of the last two arguments is reversed: // long clone(unsigned long flags, void *stack, // int *parent_tid, unsigned long tls, // int *child_tid); #if defined(__x86_64__) EXPECT_THAT(syscall(__NR_clone, SIGCHLD | CLONE_SETTLS, &stack, nullptr, nullptr, kNonCanonical), SyscallFailsWithErrno(EPERM)); #elif defined(__aarch64__) || defined(__riscv) EXPECT_THAT(syscall(__NR_clone, SIGCHLD | CLONE_SETTLS, &stack, nullptr, kNonCanonical, nullptr), SyscallFailsWithErrno(EPERM)); #endif } #ifndef SYS_clone3 #define SYS_clone3 435 #endif // SYS_clone3 // struct clone_args is a Linux clone struct. Old versions of glibc do not // expose it. See include/uapi/linux/sched.h struct clone_args { uint64_t flags; uint64_t pidfd; uint64_t child_tid; uint64_t parent_tid; uint64_t exit_signal; uint64_t stack; uint64_t stack_size; uint64_t tls; uint64_t set_tid; uint64_t set_tid_size; uint64_t cgroup; }; int clone3(struct clone_args* ca, size_t size) { return syscall(SYS_clone3, ca, size); } TEST(CloneTest, CloneParent) { // A is the test process. B is the child. C is the grandchild. // C is created with CLONE_PARENT, so it should be a adopted by A. pid_t a_pid = getpid(); int fds[2]; ASSERT_THAT(pipe(fds), SyscallSucceeds()); FileDescriptor rfd(fds[0]); FileDescriptor wfd(fds[1]); pid_t b_pid = fork(); if (b_pid == 0) { rfd.CloseSignalSafe(); clone_args ca = {}; ca.flags = CLONE_PARENT; ca.exit_signal = 0; // clone3 will fail without this. pid_t c_pid = clone3(&ca, sizeof(ca)); if (c_pid == 0) { if (getppid() != a_pid) { _exit(42); // CLONE_PARENT failed. } _exit(0); } if (c_pid < 0) { _exit(1); // C could not be created. } if (write(wfd.get(), &c_pid, sizeof(c_pid)) != sizeof(c_pid)) { _exit(1); } // B should fail to wait on C with ECHILD if CLONE_PARENT worked. int status = -1; int rval = wait4(c_pid, &status, __WALL, NULL); if (rval != -1 || errno != ECHILD) { _exit(43); } _exit(0); } ASSERT_THAT(b_pid, SyscallSucceeds()); wfd.reset(); // A's wait on B. int status; EXPECT_THAT(waitpid(b_pid, &status, 0), SyscallSucceedsWithValue(b_pid)); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); // A's wait on C. pid_t c_pid = -1; EXPECT_THAT(read(rfd.get(), &c_pid, sizeof(c_pid)), SyscallSucceedsWithValue(sizeof(c_pid))); ASSERT_GT(c_pid, 0); EXPECT_THAT(wait4(c_pid, &status, __WALL, NULL), SyscallSucceedsWithValue(c_pid)); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } TEST(CloneTest, CloneParentOrThreadWithExitSignalFails) { clone_args ca = {}; ca.flags = CLONE_PARENT; ca.exit_signal = SIGCHLD; EXPECT_THAT(clone3(&ca, sizeof(ca)), SyscallFailsWithErrno(EINVAL)); ca = {}; ca.flags = CLONE_THREAD; ca.exit_signal = SIGCHLD; EXPECT_THAT(clone3(&ca, sizeof(ca)), SyscallFailsWithErrno(EINVAL)); } TEST(CloneTest, CloneParentInitProcessFails) { clone_args ca = {}; ca.flags = CLONE_NEWPID | CLONE_NEWUSER; ca.exit_signal = SIGCHLD; int pid = clone3(&ca, sizeof(ca)); ASSERT_THAT(pid, SyscallSucceeds()); if (pid == 0) { clone_args pca = {}; pca.flags = CLONE_PARENT; pca.exit_signal = 0; int ret = clone3(&pca, sizeof(pca)); if (ret < 0 && errno == EINVAL) { _exit(0); } _exit(1); } int status; ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceeds()); EXPECT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } // Checks that clone fails for any unsupported flag. TEST(CloneTest, Clone3UnknownFlag) { clone_args ca = {}; ca.flags = (1ULL << 63); ca.exit_signal = SIGCHLD; EXPECT_THAT(clone3(&ca, sizeof(ca)), SyscallFailsWithErrno(EINVAL)); } // Clone3 works as Clone. TEST(CloneTest, Clone3AsClone) { clone_args ca = {}; ca.exit_signal = SIGCHLD; int child_pid; EXPECT_THAT(child_pid = clone3(&ca, sizeof(ca)), SyscallSucceeds()); if (child_pid == 0) { _exit(0); } int status; EXPECT_THAT(waitpid(child_pid, &status, 0), SyscallSucceedsWithValue(child_pid)); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } // Clone3 works with basic Clone3 values for args like exit_signal & parent_tid. TEST(CloneTest, Clone3Basic) { clone_args ca = {}; ca.flags = CLONE_PARENT_SETTID; ca.exit_signal = SIGCHLD; pid_t store_child_tid = 0; ca.parent_tid = reinterpret_cast(&store_child_tid); int child_pid; EXPECT_THAT(child_pid = clone3(&ca, sizeof(ca)), SyscallSucceeds()); EXPECT_EQ(store_child_tid, child_pid); if (child_pid == 0) { _exit(0); } int status; EXPECT_THAT(waitpid(child_pid, &status, 0), SyscallSucceedsWithValue(child_pid)); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } static void DummySignalHandler(int) {} // Clone3 CLONE_CLEAR_SIGHAND flag works correctly. TEST(CloneTest, Clone3ClearSighand) { // Set a signal handler for SIGUSR1 struct sigaction act_usr1 = {}; act_usr1.sa_handler = DummySignalHandler; ASSERT_THAT(sigaction(SIGUSR1, &act_usr1, nullptr), SyscallSucceeds()); // Set SIG_IGN for SIGUSR2 struct sigaction act_usr2 = {}; act_usr2.sa_handler = SIG_IGN; ASSERT_THAT(sigaction(SIGUSR2, &act_usr2, nullptr), SyscallSucceeds()); // Call clone3() with CLONE_CLEAR_SIGHAND clone_args ca = {}; ca.flags = CLONE_CLEAR_SIGHAND; ca.exit_signal = SIGCHLD; int child_pid; EXPECT_THAT(child_pid = clone3(&ca, sizeof(ca)), SyscallSucceeds()); if (child_pid == 0) { // In the child, check that SIGUSR1 was reset to SIG_DFL struct sigaction cur_usr1 = {}; if (sigaction(SIGUSR1, nullptr, &cur_usr1) < 0) { _exit(1); } if (cur_usr1.sa_handler != SIG_DFL) { _exit(2); } // And that SIGUSR2 remains set to SIG_IGN struct sigaction cur_usr2 = {}; if (sigaction(SIGUSR2, nullptr, &cur_usr2) < 0) { _exit(3); } if (cur_usr2.sa_handler != SIG_IGN) { _exit(4); } _exit(0); } // Check that the child's assertions passed (i.e. it exited 0) int status; EXPECT_THAT(waitpid(child_pid, &status, 0), SyscallSucceedsWithValue(child_pid)); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << "status = " << status; // Verify that the parent's SIGUSR1 and SIGUSR2 handlers are unchanged struct sigaction parent_usr1 = {}; ASSERT_THAT(sigaction(SIGUSR1, nullptr, &parent_usr1), SyscallSucceeds()); EXPECT_EQ(parent_usr1.sa_handler, DummySignalHandler); struct sigaction parent_usr2 = {}; ASSERT_THAT(sigaction(SIGUSR2, nullptr, &parent_usr2), SyscallSucceeds()); EXPECT_EQ(parent_usr2.sa_handler, SIG_IGN); } // CLONE_CLEAR_SIGHAND and CLONE_SIGHAND are mutually exclusive. TEST(CloneTest, Clone3ClearSighandIncompatible) { clone_args ca = {}; ca.flags = CLONE_CLEAR_SIGHAND | CLONE_SIGHAND | CLONE_VM; ca.exit_signal = SIGCHLD; EXPECT_THAT(clone3(&ca, sizeof(ca)), SyscallFailsWithErrno(EINVAL)); } } // namespace } // namespace testing } // namespace gvisor