diff options
Diffstat (limited to 'test/syscalls/linux/fork.cc')
-rw-r--r-- | test/syscalls/linux/fork.cc | 448 |
1 files changed, 0 insertions, 448 deletions
diff --git a/test/syscalls/linux/fork.cc b/test/syscalls/linux/fork.cc deleted file mode 100644 index 906f3358d..000000000 --- a/test/syscalls/linux/fork.cc +++ /dev/null @@ -1,448 +0,0 @@ -// 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 <errno.h> -#include <fcntl.h> -#include <sched.h> -#include <stdlib.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <atomic> -#include <cstdlib> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/capability_util.h" -#include "test/util/logging.h" -#include "test/util/memory_util.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<char*>(mmap(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0)); - ASSERT_NE(reinterpret_cast<void*>(shared_), MAP_FAILED); - - // Make a private mapping. - private_ = - reinterpret_cast<char*>(mmap(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); - ASSERT_NE(reinterpret_cast<void*>(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 proccess. - 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. -TEST_F(ForkTest, PrivateMemory) { - std::atomic<uint32_t> 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. -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<char> 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 == SIGSTKSZ); - TEST_CHECK(oss.ss_sp == stack.ss_sp); - - Exit(0); - } - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -TEST_F(ForkTest, Affinity) { - // 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<void*>(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; -} - -#ifdef __x86_64__ -// Clone with CLONE_SETTLS and a non-canonical TLS address is rejected. -TEST(CloneTest, NonCanonicalTLS) { - constexpr uintptr_t kNonCanonical = 1ull << 48; - - // We need a valid address for the stack pointer. We'll never actually execute - // on this. - char stack; - - EXPECT_THAT(syscall(__NR_clone, SIGCHLD | CLONE_SETTLS, &stack, nullptr, - nullptr, kNonCanonical), - SyscallFailsWithErrno(EPERM)); -} -#endif - -} // namespace -} // namespace testing -} // namespace gvisor |