Open source system call tests.

PiperOrigin-RevId: 224886231
Change-Id: I0fccb4d994601739d8b16b1d4e6b31f40297fb22
This commit is contained in:
Brian Geffon
2018-12-10 14:42:34 -08:00
committed by Shentubot
parent 833edbd10b
commit d3bc79bc84
253 changed files with 55023 additions and 3 deletions
+6 -3
View File
@@ -28,8 +28,8 @@ bazel version
cd git/repo
# Build everything.
bazel build //...
# Build everything except //test.
bazel build //pkg/... //runsc/... //tools/...
# Test use this variable to determine what runtime to use.
runtime=runsc_test_$((RANDOM))
@@ -45,7 +45,10 @@ uninstallRuntime() {
# We turn off "-e" flag because we must move the log files even if the test
# fails.
set +e
bazel test --test_output=errors //...
# Note: We do not run the tests in the //test folder as these would take
# too long.
bazel test --test_output=errors //pkg/... //runsc/... //tools/...
exit_code=${?}
# This function spawns a subshell to install crictl and containerd.
+1
View File
@@ -21,6 +21,7 @@ go_library(
importpath = "gvisor.googlesource.com/gvisor/runsc/boot",
visibility = [
"//runsc:__subpackages__",
"//test:__subpackages__",
],
deps = [
"//pkg/abi",
+1
View File
@@ -13,6 +13,7 @@ go_library(
importpath = "gvisor.googlesource.com/gvisor/runsc/container",
visibility = [
"//runsc:__subpackages__",
"//test:__subpackages__",
],
deps = [
"//pkg/log",
+1
View File
@@ -11,6 +11,7 @@ go_library(
importpath = "gvisor.googlesource.com/gvisor/runsc/specutils",
visibility = [
"//runsc:__subpackages__",
"//test:__subpackages__",
],
deps = [
"//pkg/abi/linux",
+1
View File
@@ -13,6 +13,7 @@ go_library(
importpath = "gvisor.googlesource.com/gvisor/runsc/test/testutil",
visibility = [
"//runsc:__subpackages__",
"//test:__subpackages__",
],
deps = [
"//runsc/boot",
+522
View File
File diff suppressed because it is too large Load Diff
+110
View File
@@ -0,0 +1,110 @@
# gVisor system call test suite
This is a test suite for Linux system calls. It runs under both gVisor and
Linux, and ensures compatability between the two.
When adding support for a new syscall (or syscall argument) to gVisor, a
corresponding syscall test should be added. It's usually recommended to write
the test first and make sure that it passes on Linux before making changes to
gVisor.
This document outlines the general guidelines for tests and specific rules that
must be followed for new tests.
## Running the tests
Each test file generates three different test targets that run in different
environments:
* a `native` target that runs directly on the host machine,
* a `runsc_ptrace` target that runs inside runsc using the ptrace platform, and
* a `runsc_kvm` target that runs inside runsc using the KVM platform.
For example, the test in `access_test.cc` generates the following targets:
* `//test/syscalls:access_test_native`
* `//test/syscalls:access_test_runsc_ptrace`
* `//test/syscalls:access_test_runsc_kvm`
Any of these targets can be run directly via `bazel test`.
```bash
$ bazel test //test/syscalls:access_test_native
$ bazel test //test/syscalls:access_test_runsc_ptrace
$ bazel test //test/syscalls:access_test_runsc_kvm
```
To run all the tests on a particular platform, you can filter by the platform
tag:
```bash
# Run all tests in native environment:
$ bazel test --test_tag_filter=native //test/syscalls:*
# Run all tests in runsc with ptrace:
$ bazel test --test_tag_filter=runsc_ptrace //test/syscalls:*
# Run all tests in runsc with kvm:
$ bazel test --test_tag_filter=runsc_kvm //test/syscalls:*
```
You can also run all the tests on every platform. (Warning, this may take a
while to run.)
```bash
# Run all tests on every platform:
$ bazel test //test/syscalls:*
```
## Writing new tests
Whenever we add support for a new syscall, or add support for a new argument or
option for a syscall, we should always add a new test (perhaps many new tests).
In general, it is best to write the test first and make sure it passes on Linux
by running the test on the `native` platform on a Linux machine. This ensures
that the gVisor implementation matches actual Linux behavior. Sometimes man
pages contain errors, so always check the actual Linux behavior.
gVisor uses the [Google Test][googletest] test framework, with a few custom
matchers and guidelines, described below.
### Syscall matchers
When testing an individual system call, use the following syscall matchers,
which will match the value returned by the syscall and the errno.
```cc
SyscallSucceeds()
SyscallSucceedsWithValue(...)
SyscallFails()
SyscallFailsWithErrno(...)
```
### Use test utilities (RAII classes)
The test utilties are written as RAII classes. These utilities should be
preferred over custom test harnesses.
Local class instances should be preferred, whereever possible, over full test
fixtures.
A test utility should be created when there is more than one test that requires
that same functionality, otherwise the class should be test local.
## Save/Restore support in tests
gVisor supports save/restore, and our syscall tests are written in a way to
enable saving/restoring at certain points. Hence, there are calls to
`MaybeSave`, and certain tests that should not trigger saves are named with
`NoSave`.
However, the current open-source test runner does not yet support triggering
save/restore, so these functions and annotations have no effect on the
open-source tests.
We plan on extending our open-source test runner to trigger save/restore. Until
then, these functions and annotations should be ignored.
[googletest]: https://github.com/abseil/googletest
+54
View File
@@ -0,0 +1,54 @@
"""Defines a rule for syscall test targets."""
# syscall_test is a macro that will create targets to run the given test target
# on the host (native) and runsc.
def syscall_test(test, size = "small"):
_syscall_test(test, size, "native")
_syscall_test(test, size, "kvm")
_syscall_test(test, size, "ptrace")
def _syscall_test(test, size, platform):
test_name = test.split(":")[1]
# Prepend "runsc" to non-native platform names.
full_platform = platform if platform == "native" else "runsc_" + platform
# Add the full_platform in a tag to make it easier to run all the tests on
# a specific platform.
tags = [full_platform]
# Add tag to prevent the tests from running in a Bazel sandbox.
# TODO: Make the tests run without this tag.
tags.append("no-sandbox")
# TODO: KVM tests are tagged "manual" to until the platform is
# more stable.
if platform == "kvm":
tags += ["manual"]
sh_test(
srcs = ["syscall_test_runner.sh"],
name = test_name + "_" + full_platform,
data = [
":syscall_test",
test,
],
args = [
# First argument is location to syscall_test binary.
"$(location :syscall_test)",
# Rest of arguments are passed directly to syscall_test binary.
"--test-name=" + test_name,
"--platform=" + platform,
"--debug=false",
"--strace=false",
"--parallel=true",
],
size = size,
tags = tags,
)
def sh_test(**kwargs):
"""Wraps the standard sh_test."""
native.sh_test(
**kwargs
)
+12
View File
@@ -0,0 +1,12 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
package(licenses = ["notice"]) # Apache 2.0
go_library(
name = "gtest",
srcs = ["gtest.go"],
importpath = "gvisor.googlesource.com/gvisor/test/syscalls/gtest",
visibility = [
"//test:__subpackages__",
],
)
+93
View File
@@ -0,0 +1,93 @@
// Copyright 2018 Google LLC
//
// 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.
// Package gtest contains helpers for running google-test tests from Go.
package gtest
import (
"fmt"
"os/exec"
"strings"
)
var (
// ListTestFlag is the flag that will list tests in gtest binaries.
ListTestFlag = "--gtest_list_tests"
// FilterTestFlag is the flag that will filter tests in gtest binaries.
FilterTestFlag = "--gtest_filter"
)
// TestCase is a single gtest test case.
type TestCase struct {
// Suite is the suite for this test.
Suite string
// Name is the name of this individual test.
Name string
}
// FullName returns the name of the test including the suite. It is suitable to
// pass to "-gtest_filter".
func (tc TestCase) FullName() string {
return fmt.Sprintf("%s.%s", tc.Suite, tc.Name)
}
// ParseTestCases calls a gtest test binary to list its test and returns a
// slice with the name and suite of each test.
func ParseTestCases(testBin string, extraArgs ...string) ([]TestCase, error) {
args := append([]string{ListTestFlag}, extraArgs...)
cmd := exec.Command(testBin, args...)
out, err := cmd.Output()
if err != nil {
exitErr, ok := err.(*exec.ExitError)
if !ok {
return nil, fmt.Errorf("could not enumerate gtest tests: %v", err)
}
return nil, fmt.Errorf("could not enumerate gtest tests: %v\nstderr:\n%s", err, exitErr.Stderr)
}
var t []TestCase
var suite string
for _, line := range strings.Split(string(out), "\n") {
// Strip comments.
line = strings.Split(line, "#")[0]
// New suite?
if !strings.HasPrefix(line, " ") {
suite = strings.TrimSuffix(strings.TrimSpace(line), ".")
continue
}
// Individual test.
name := strings.TrimSpace(line)
// Do we have a suite yet?
if suite == "" {
return nil, fmt.Errorf("test without a suite: %v", name)
}
// Add this individual test.
t = append(t, TestCase{
Suite: suite,
Name: name,
})
}
if len(t) == 0 {
return nil, fmt.Errorf("no tests parsed from %v", testBin)
}
return t, nil
}
+226
View File
@@ -0,0 +1,226 @@
// Copyright 2018 Google LLC
//
// 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 <string.h>
#include <sys/mman.h>
#include "test/util/memory_util.h"
#include "test/util/posix_error.h"
#include "test/util/test_util.h"
#include "gtest/gtest.h"
#ifndef __x86_64__
#error "This test is x86-64 specific."
#endif
namespace gvisor {
namespace testing {
namespace {
constexpr char kInt3 = '\xcc';
constexpr char kInt80[2] = {'\xcd', '\x80'};
constexpr char kSyscall[2] = {'\x0f', '\x05'};
constexpr char kSysenter[2] = {'\x0f', '\x34'};
void ExitGroup32(const char instruction[2], int code) {
const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0));
// Fill with INT 3 in case we execute too far.
memset(m.ptr(), kInt3, m.len());
memcpy(m.ptr(), instruction, 2);
// We're playing *extremely* fast-and-loose with the various syscall ABIs
// here, which we can more-or-less get away with since exit_group doesn't
// return.
//
// SYSENTER expects the user stack in (%ebp) and arg6 in 0(%ebp). The kernel
// will unconditionally dereference %ebp for arg6, so we must pass a valid
// address or it will return EFAULT.
//
// SYSENTER also unconditionally returns to thread_info->sysenter_return which
// is ostensibly a stub in the 32-bit VDSO. But a 64-bit binary doesn't have
// the 32-bit VDSO mapped, so sysenter_return will simply be the value
// inherited from the most recent 32-bit ancestor, or NULL if there is none.
// As a result, return would not return from SYSENTER.
asm volatile(
"movl $252, %%eax\n" // exit_group
"movl %[code], %%ebx\n" // code
"movl %%edx, %%ebp\n" // SYSENTER: user stack (use IP as a valid addr)
"leaq -20(%%rsp), %%rsp\n"
"movl $0x2b, 16(%%rsp)\n" // SS = CPL3 data segment
"movl $0,12(%%rsp)\n" // ESP = nullptr (unused)
"movl $0, 8(%%rsp)\n" // EFLAGS
"movl $0x23, 4(%%rsp)\n" // CS = CPL3 32-bit code segment
"movl %%edx, 0(%%rsp)\n" // EIP
"iretl\n"
"int $3\n"
:
: [code] "m"(code), [ip] "d"(m.ptr())
: "rax", "rbx", "rsp");
}
constexpr int kExitCode = 42;
TEST(Syscall32Bit, Int80) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO: 32-bit segments are broken (but not explictly
// disabled).
return;
case Platform::kPtrace:
// TODO: The ptrace platform does not have a consistent story
// here.
return;
case Platform::kNative:
break;
}
// Upstream Linux. 32-bit syscalls allowed.
EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42),
"");
}
TEST(Syscall32Bit, Sysenter) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO: See above.
return;
case Platform::kPtrace:
// TODO: See above.
return;
case Platform::kNative:
break;
}
if (GetCPUVendor() == CPUVendor::kAMD) {
// SYSENTER is an illegal instruction in compatibility mode on AMD.
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
::testing::KilledBySignal(SIGILL), "");
return;
}
// Upstream Linux on !AMD, 32-bit syscalls allowed.
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), ::testing::ExitedWithCode(42),
"");
}
TEST(Syscall32Bit, Syscall) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO: See above.
return;
case Platform::kPtrace:
// TODO: See above.
return;
case Platform::kNative:
break;
}
if (GetCPUVendor() == CPUVendor::kIntel) {
// SYSCALL is an illegal instruction in compatibility mode on Intel.
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
::testing::KilledBySignal(SIGILL), "");
return;
}
// Upstream Linux on !Intel, 32-bit syscalls allowed.
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), ::testing::ExitedWithCode(42),
"");
}
// Far call code called below.
//
// Input stack layout:
//
// %esp+12 lcall segment
// %esp+8 lcall address offset
// %esp+0 return address
//
// The lcall will enter compatibility mode and jump to the call address (the
// address of the lret). The lret will return to 64-bit mode at the retq, which
// will return to the external caller of this function.
//
// Since this enters compatibility mode, it must be mapped in a 32-bit region of
// address space and have a 32-bit stack pointer.
constexpr char kFarCall[] = {
'\x67', '\xff', '\x5c', '\x24', '\x08', // lcall *8(%esp)
'\xc3', // retq
'\xcb', // lret
};
void FarCall32() {
const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0));
// Fill with INT 3 in case we execute too far.
memset(m.ptr(), kInt3, m.len());
// 32-bit code.
memcpy(m.ptr(), kFarCall, sizeof(kFarCall));
// Use the end of the code page as its stack.
uintptr_t stack = m.endaddr();
uintptr_t lcall = m.addr();
uintptr_t lret = m.addr() + sizeof(kFarCall) - 1;
// N.B. We must save and restore RSP manually. GCC can do so automatically
// with an "rsp" clobber, but clang cannot.
asm volatile(
// Place the address of lret (%edx) and the 32-bit code segment (0x23) on
// the 32-bit stack for lcall.
"subl $0x8, %%ecx\n"
"movl $0x23, 4(%%ecx)\n"
"movl %%edx, 0(%%ecx)\n"
// Save the current stack and switch to 32-bit stack.
"pushq %%rbp\n"
"movq %%rsp, %%rbp\n"
"movq %%rcx, %%rsp\n"
// Run the lcall code.
"callq *%%rbx\n"
// Restore the old stack.
"leaveq\n"
: "+c"(stack)
: "b"(lcall), "d"(lret));
}
TEST(Call32Bit, Disallowed) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO: See above.
return;
case Platform::kPtrace:
// The ptrace platform cannot prevent switching to compatibility mode.
ABSL_FALLTHROUGH_INTENDED;
case Platform::kNative:
break;
}
// Shouldn't crash.
FarCall32();
}
} // namespace
} // namespace testing
} // namespace gvisor
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+93
View File
@@ -0,0 +1,93 @@
// Copyright 2018 Google LLC
//
// 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 <stdio.h>
#include <sys/un.h>
#include <algorithm>
#include <vector>
#include "gtest/gtest.h"
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
namespace {
TEST_P(AllSocketPairTest, BoundSenderAddrCoalesced) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(),
sockets->first_addr_size()),
SyscallSucceeds());
ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds());
ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(),
sockets->first_addr_size()),
SyscallSucceeds());
int accepted = -1;
ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr),
SyscallSucceeds());
FileDescriptor closer(accepted);
int i = 0;
ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0),
SyscallSucceedsWithValue(sizeof(i)));
ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(),
sockets->second_addr_size()),
SyscallSucceeds());
i = 0;
ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0),
SyscallSucceedsWithValue(sizeof(i)));
int ri[2] = {0, 0};
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
ASSERT_THAT(
RetryEINTR(recvfrom)(accepted, ri, sizeof(ri), 0,
reinterpret_cast<sockaddr*>(&addr), &addr_len),
SyscallSucceedsWithValue(sizeof(ri)));
EXPECT_EQ(addr_len, sockets->second_addr_len());
EXPECT_EQ(
memcmp(&addr, sockets->second_addr(),
std::min((size_t)addr_len, (size_t)sockets->second_addr_len())),
0);
}
INSTANTIATE_TEST_CASE_P(
AllUnixDomainSockets, AllSocketPairTest,
::testing::ValuesIn(VecCat<SocketPairKind>(
ApplyVec<SocketPairKind>(
FilesystemUnboundUnixDomainSocketPair,
AllBitwiseCombinations(List<int>{SOCK_STREAM},
List<int>{0, SOCK_NONBLOCK},
List<int>{0, SOCK_CLOEXEC})),
ApplyVec<SocketPairKind>(
AbstractUnboundUnixDomainSocketPair,
AllBitwiseCombinations(List<int>{SOCK_STREAM},
List<int>{0, SOCK_NONBLOCK},
List<int>{0, SOCK_CLOEXEC})))));
} // namespace
} // namespace testing
} // namespace gvisor
+170
View File
@@ -0,0 +1,170 @@
// Copyright 2018 Google LLC
//
// 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 <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "test/util/capability_util.h"
#include "test/util/fs_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
using ::testing::Ge;
namespace gvisor {
namespace testing {
namespace {
class AccessTest : public ::testing::Test {
public:
std::string CreateTempFile(int perm) {
const std::string path = NewTempAbsPath();
const int fd = open(path.c_str(), O_CREAT | O_RDONLY, perm);
TEST_PCHECK(fd > 0);
TEST_PCHECK(close(fd) == 0);
return path;
}
protected:
// SetUp creates various configurations of files.
void SetUp() override {
// Move to the temporary directory. This allows us to reason more easily
// about absolute and relative paths.
ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds());
// Create an empty file, standard permissions.
relfile_ = NewTempRelPath();
int fd;
ASSERT_THAT(fd = open(relfile_.c_str(), O_CREAT | O_TRUNC, 0644),
SyscallSucceedsWithValue(Ge(0)));
ASSERT_THAT(close(fd), SyscallSucceeds());
absfile_ = GetAbsoluteTestTmpdir() + "/" + relfile_;
// Create an empty directory, no writable permissions.
absdir_ = NewTempAbsPath();
reldir_ = JoinPath(Basename(absdir_), "");
ASSERT_THAT(mkdir(reldir_.c_str(), 0555), SyscallSucceeds());
// This file doesn't exist.
relnone_ = NewTempRelPath();
absnone_ = GetAbsoluteTestTmpdir() + "/" + relnone_;
}
// TearDown unlinks created files.
void TearDown() override {
ASSERT_THAT(unlink(absfile_.c_str()), SyscallSucceeds());
ASSERT_THAT(rmdir(absdir_.c_str()), SyscallSucceeds());
}
std::string relfile_;
std::string reldir_;
std::string absfile_;
std::string absdir_;
std::string relnone_;
std::string absnone_;
};
TEST_F(AccessTest, RelativeFile) {
EXPECT_THAT(access(relfile_.c_str(), R_OK), SyscallSucceeds());
}
TEST_F(AccessTest, RelativeDir) {
EXPECT_THAT(access(reldir_.c_str(), R_OK | X_OK), SyscallSucceeds());
}
TEST_F(AccessTest, AbsFile) {
EXPECT_THAT(access(absfile_.c_str(), R_OK), SyscallSucceeds());
}
TEST_F(AccessTest, AbsDir) {
EXPECT_THAT(access(absdir_.c_str(), R_OK | X_OK), SyscallSucceeds());
}
TEST_F(AccessTest, RelDoesNotExist) {
EXPECT_THAT(access(relnone_.c_str(), R_OK), SyscallFailsWithErrno(ENOENT));
}
TEST_F(AccessTest, AbsDoesNotExist) {
EXPECT_THAT(access(absnone_.c_str(), R_OK), SyscallFailsWithErrno(ENOENT));
}
TEST_F(AccessTest, InvalidMode) {
EXPECT_THAT(access(relfile_.c_str(), 0xffffffff),
SyscallFailsWithErrno(EINVAL));
}
TEST_F(AccessTest, NoPerms) {
// Drop capabilities that allow us to override permissions. We must drop
// PERMITTED because access() checks those instead of EFFECTIVE.
ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_OVERRIDE));
ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_READ_SEARCH));
EXPECT_THAT(access(absdir_.c_str(), W_OK), SyscallFailsWithErrno(EACCES));
}
TEST_F(AccessTest, InvalidName) {
EXPECT_THAT(access(reinterpret_cast<char*>(0x1234), W_OK),
SyscallFailsWithErrno(EFAULT));
}
TEST_F(AccessTest, UsrReadOnly) {
// Drop capabilities that allow us to override permissions. We must drop
// PERMITTED because access() checks those instead of EFFECTIVE.
ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_OVERRIDE));
ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_READ_SEARCH));
const std::string filename = CreateTempFile(0400);
EXPECT_THAT(access(filename.c_str(), R_OK), SyscallSucceeds());
EXPECT_THAT(access(filename.c_str(), W_OK), SyscallFailsWithErrno(EACCES));
EXPECT_THAT(access(filename.c_str(), X_OK), SyscallFailsWithErrno(EACCES));
EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds());
}
TEST_F(AccessTest, UsrReadExec) {
// Drop capabilities that allow us to override permissions. We must drop
// PERMITTED because access() checks those instead of EFFECTIVE.
ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_OVERRIDE));
ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_READ_SEARCH));
const std::string filename = CreateTempFile(0500);
EXPECT_THAT(access(filename.c_str(), R_OK | X_OK), SyscallSucceeds());
EXPECT_THAT(access(filename.c_str(), W_OK), SyscallFailsWithErrno(EACCES));
EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds());
}
TEST_F(AccessTest, UsrReadWrite) {
const std::string filename = CreateTempFile(0600);
EXPECT_THAT(access(filename.c_str(), R_OK | W_OK), SyscallSucceeds());
EXPECT_THAT(access(filename.c_str(), X_OK), SyscallFailsWithErrno(EACCES));
EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds());
}
TEST_F(AccessTest, UsrReadWriteExec) {
const std::string filename = CreateTempFile(0700);
EXPECT_THAT(access(filename.c_str(), R_OK | W_OK | X_OK), SyscallSucceeds());
EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds());
}
} // namespace
} // namespace testing
} // namespace gvisor
+241
View File
@@ -0,0 +1,241 @@
// Copyright 2018 Google LLC
//
// 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 <sched.h>
#include <sys/types.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "absl/strings/str_split.h"
#include "test/util/cleanup.h"
#include "test/util/fs_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 {
// These tests are for both the sched_getaffinity(2) and sched_setaffinity(2)
// syscalls.
class AffinityTest : public ::testing::Test {
protected:
void SetUp() override {
EXPECT_THAT(
// Needs use the raw syscall to get the actual size.
cpuset_size_ = syscall(SYS_sched_getaffinity, /*pid=*/0,
sizeof(cpu_set_t), &mask_),
SyscallSucceeds());
// Lots of tests rely on having more than 1 logical processor available.
EXPECT_GT(CPU_COUNT(&mask_), 1);
}
static PosixError ClearLowestBit(cpu_set_t* mask, size_t cpus) {
const size_t mask_size = CPU_ALLOC_SIZE(cpus);
for (size_t n = 0; n < cpus; ++n) {
if (CPU_ISSET_S(n, mask_size, mask)) {
CPU_CLR_S(n, mask_size, mask);
return NoError();
}
}
return PosixError(EINVAL, "No bit to clear, mask is empty");
}
PosixError ClearLowestBit() { return ClearLowestBit(&mask_, CPU_SETSIZE); }
// Stores the initial cpu mask for this process.
cpu_set_t mask_ = {};
int cpuset_size_ = 0;
};
// sched_getaffinity(2) is implemented.
TEST_F(AffinityTest, SchedGetAffinityImplemented) {
EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
SyscallSucceeds());
}
// PID is not found.
TEST_F(AffinityTest, SchedGetAffinityInvalidPID) {
// Flaky, but it's tough to avoid a race condition when finding an unused pid
EXPECT_THAT(sched_getaffinity(/*pid=*/INT_MAX - 1, sizeof(cpu_set_t), &mask_),
SyscallFailsWithErrno(ESRCH));
}
// PID is not found.
TEST_F(AffinityTest, SchedSetAffinityInvalidPID) {
// Flaky, but it's tough to avoid a race condition when finding an unused pid
EXPECT_THAT(sched_setaffinity(/*pid=*/INT_MAX - 1, sizeof(cpu_set_t), &mask_),
SyscallFailsWithErrno(ESRCH));
}
TEST_F(AffinityTest, SchedSetAffinityZeroMask) {
CPU_ZERO(&mask_);
EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
SyscallFailsWithErrno(EINVAL));
}
// N.B. This test case relies on cpuset_size_ larger than the actual number of
// of all existing CPUs. Check your machine if the test fails.
TEST_F(AffinityTest, SchedSetAffinityNonexistentCPUDropped) {
cpu_set_t mask = mask_;
// Add a nonexistent CPU.
//
// The number needs to be larger than the possible number of CPU available,
// but smaller than the number of the CPU that the kernel claims to support --
// it's implicitly returned by raw sched_getaffinity syscall.
CPU_SET(cpuset_size_ * 8 - 1, &mask);
EXPECT_THAT(
// Use raw syscall because it will be rejected by the libc wrapper
// otherwise.
syscall(SYS_sched_setaffinity, /*pid=*/0, sizeof(cpu_set_t), &mask),
SyscallSucceeds())
<< "failed with cpumask : " << CPUSetToString(mask)
<< ", cpuset_size_ : " << cpuset_size_;
cpu_set_t newmask;
EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &newmask),
SyscallSucceeds());
EXPECT_TRUE(CPU_EQUAL(&mask_, &newmask))
<< "got: " << CPUSetToString(newmask)
<< " != expected: " << CPUSetToString(mask_);
}
TEST_F(AffinityTest, SchedSetAffinityOnlyNonexistentCPUFails) {
// Make an empty cpu set.
CPU_ZERO(&mask_);
// Add a nonexistent CPU.
//
// The number needs to be larger than the possible number of CPU available,
// but smaller than the number of the CPU that the kernel claims to support --
// it's implicitly returned by raw sched_getaffinity syscall.
int cpu = cpuset_size_ * 8 - 1;
if (cpu <= NumCPUs()) {
LOG(INFO) << "Skipping test: cpu " << cpu << " exists";
return;
}
CPU_SET(cpu, &mask_);
EXPECT_THAT(
// Use raw syscall because it will be rejected by the libc wrapper
// otherwise.
syscall(SYS_sched_setaffinity, /*pid=*/0, sizeof(cpu_set_t), &mask_),
SyscallFailsWithErrno(EINVAL));
}
TEST_F(AffinityTest, SchedSetAffinityInvalidSize) {
EXPECT_GT(cpuset_size_, 0);
// Not big enough.
EXPECT_THAT(sched_getaffinity(/*pid=*/0, cpuset_size_ - 1, &mask_),
SyscallFailsWithErrno(EINVAL));
// Not a multiple of word size.
EXPECT_THAT(sched_getaffinity(/*pid=*/0, cpuset_size_ + 1, &mask_),
SyscallFailsWithErrno(EINVAL));
}
TEST_F(AffinityTest, Sanity) {
ASSERT_NO_ERRNO(ClearLowestBit());
EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
SyscallSucceeds());
cpu_set_t newmask;
EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &newmask),
SyscallSucceeds());
EXPECT_TRUE(CPU_EQUAL(&mask_, &newmask))
<< "got: " << CPUSetToString(newmask)
<< " != expected: " << CPUSetToString(mask_);
}
TEST_F(AffinityTest, NewThread) {
ASSERT_NO_ERRNO(ClearLowestBit());
ASSERT_NO_ERRNO(ClearLowestBit());
EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_),
SyscallSucceeds());
ScopedThread([this]() {
cpu_set_t child_mask;
ASSERT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &child_mask),
SyscallSucceeds());
ASSERT_TRUE(CPU_EQUAL(&child_mask, &mask_))
<< "child cpu mask: " << CPUSetToString(child_mask)
<< " != parent cpu mask: " << CPUSetToString(mask_);
});
}
TEST_F(AffinityTest, ConsistentWithProcCpuInfo) {
// Count how many cpus are shown in /proc/cpuinfo.
std::string cpuinfo = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/cpuinfo"));
int count = 0;
for (auto const& line : absl::StrSplit(cpuinfo, '\n')) {
if (absl::StartsWith(line, "processor")) {
count++;
}
}
EXPECT_GE(count, CPU_COUNT(&mask_));
}
TEST_F(AffinityTest, ConsistentWithProcStat) {
// Count how many cpus are shown in /proc/stat.
std::string stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat"));
int count = 0;
for (auto const& line : absl::StrSplit(stat, '\n')) {
if (absl::StartsWith(line, "cpu") && !absl::StartsWith(line, "cpu ")) {
count++;
}
}
EXPECT_GE(count, CPU_COUNT(&mask_));
}
TEST_F(AffinityTest, SmallCpuMask) {
const int num_cpus = NumCPUs();
const size_t mask_size = CPU_ALLOC_SIZE(num_cpus);
cpu_set_t* mask = CPU_ALLOC(num_cpus);
ASSERT_NE(mask, nullptr);
const auto free_mask = Cleanup([&] { CPU_FREE(mask); });
CPU_ZERO_S(mask_size, mask);
ASSERT_THAT(sched_getaffinity(0, mask_size, mask), SyscallSucceeds());
}
TEST_F(AffinityTest, LargeCpuMask) {
// Allocate mask bigger than cpu_set_t normally allocates.
const size_t cpus = CPU_SETSIZE * 8;
const size_t mask_size = CPU_ALLOC_SIZE(cpus);
cpu_set_t* large_mask = CPU_ALLOC(cpus);
auto free_mask = Cleanup([large_mask] { CPU_FREE(large_mask); });
CPU_ZERO_S(mask_size, large_mask);
// Check that get affinity with large mask works as expected.
ASSERT_THAT(sched_getaffinity(/*pid=*/0, mask_size, large_mask),
SyscallSucceeds());
EXPECT_TRUE(CPU_EQUAL(&mask_, large_mask))
<< "got: " << CPUSetToString(*large_mask, cpus)
<< " != expected: " << CPUSetToString(mask_);
// Check that set affinity with large mask works as expected.
ASSERT_NO_ERRNO(ClearLowestBit(large_mask, cpus));
EXPECT_THAT(sched_setaffinity(/*pid=*/0, mask_size, large_mask),
SyscallSucceeds());
cpu_set_t* new_mask = CPU_ALLOC(cpus);
auto free_new_mask = Cleanup([new_mask] { CPU_FREE(new_mask); });
CPU_ZERO_S(mask_size, new_mask);
EXPECT_THAT(sched_getaffinity(/*pid=*/0, mask_size, new_mask),
SyscallSucceeds());
EXPECT_TRUE(CPU_EQUAL_S(mask_size, large_mask, new_mask))
<< "got: " << CPUSetToString(*new_mask, cpus)
<< " != expected: " << CPUSetToString(*large_mask, cpus);
}
} // namespace
} // namespace testing
} // namespace gvisor
+433
View File
@@ -0,0 +1,433 @@
// Copyright 2018 Google LLC
//
// 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 <fcntl.h>
#include <linux/aio_abi.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "test/syscalls/linux/file_base.h"
#include "test/util/cleanup.h"
#include "test/util/file_descriptor.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
namespace {
constexpr char kData[] = "hello world!";
int SubmitCtx(aio_context_t ctx, long nr, struct iocb** iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}
} // namespace
class AIOTest : public FileTest {
public:
AIOTest() : ctx_(0) {}
int SetupContext(unsigned int nr) {
return syscall(__NR_io_setup, nr, &ctx_);
}
int Submit(long nr, struct iocb** iocbpp) {
return SubmitCtx(ctx_, nr, iocbpp);
}
int GetEvents(long min, long max, struct io_event* events,
struct timespec* timeout) {
return RetryEINTR(syscall)(__NR_io_getevents, ctx_, min, max, events,
timeout);
}
int DestroyContext() { return syscall(__NR_io_destroy, ctx_); }
void TearDown() override {
FileTest::TearDown();
if (ctx_ != 0) {
ASSERT_THAT(DestroyContext(), SyscallSucceeds());
}
}
struct iocb CreateCallback() {
struct iocb cb = {};
cb.aio_data = 0x123;
cb.aio_fildes = test_file_fd_.get();
cb.aio_lio_opcode = IOCB_CMD_PWRITE;
cb.aio_buf = reinterpret_cast<uint64_t>(kData);
cb.aio_offset = 0;
cb.aio_nbytes = strlen(kData);
return cb;
}
protected:
aio_context_t ctx_;
};
TEST_F(AIOTest, BasicWrite) {
// Copied from fs/aio.c.
constexpr unsigned AIO_RING_MAGIC = 0xa10a10a1;
struct aio_ring {
unsigned id;
unsigned nr;
unsigned head;
unsigned tail;
unsigned magic;
unsigned compat_features;
unsigned incompat_features;
unsigned header_length;
struct io_event io_events[0];
};
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
// Check that 'ctx_' points to a valid address. libaio uses it to check if
// aio implementation uses aio_ring. gVisor doesn't and returns all zeroes.
// Linux implements aio_ring, so skip the zeroes check.
//
// TODO: Remove when gVisor implements aio_ring.
auto ring = reinterpret_cast<struct aio_ring*>(ctx_);
auto magic = IsRunningOnGvisor() ? 0 : AIO_RING_MAGIC;
EXPECT_EQ(ring->magic, magic);
struct iocb cb = CreateCallback();
struct iocb* cbs[1] = {&cb};
// Submit the request.
ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1));
// Get the reply.
struct io_event events[1];
ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1));
// Verify that it is as expected.
EXPECT_EQ(events[0].data, 0x123);
EXPECT_EQ(events[0].obj, reinterpret_cast<long>(&cb));
EXPECT_EQ(events[0].res, strlen(kData));
// Verify that the file contains the contents.
char verify_buf[32] = {};
ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)),
SyscallSucceeds());
EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0);
}
TEST_F(AIOTest, BadWrite) {
// Create a pipe and immediately close the read end.
int pipefd[2];
ASSERT_THAT(pipe(pipefd), SyscallSucceeds());
FileDescriptor rfd(pipefd[0]);
FileDescriptor wfd(pipefd[1]);
rfd.reset(); // Close the read end.
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
struct iocb cb = CreateCallback();
// Try to write to the read end.
cb.aio_fildes = wfd.get();
struct iocb* cbs[1] = {&cb};
// Submit the request.
ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1));
// Get the reply.
struct io_event events[1];
ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1));
// Verify that it fails with the right error code.
EXPECT_EQ(events[0].data, 0x123);
EXPECT_EQ(events[0].obj, reinterpret_cast<uint64_t>(&cb));
EXPECT_LT(events[0].res, 0);
}
TEST_F(AIOTest, ExitWithPendingIo) {
// Setup a context that is 5 entries deep.
ASSERT_THAT(SetupContext(5), SyscallSucceeds());
struct iocb cb = CreateCallback();
struct iocb* cbs[] = {&cb};
// Submit a request but don't complete it to make it pending.
EXPECT_THAT(Submit(1, cbs), SyscallSucceeds());
}
int Submitter(void* arg) {
auto test = reinterpret_cast<AIOTest*>(arg);
struct iocb cb = test->CreateCallback();
struct iocb* cbs[1] = {&cb};
// Submit the request.
TEST_CHECK(test->Submit(1, cbs) == 1);
return 0;
}
TEST_F(AIOTest, CloneVm) {
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
const size_t kStackSize = 5 * kPageSize;
std::unique_ptr<char[]> stack(new char[kStackSize]);
char* bp = stack.get() + kStackSize;
pid_t child;
ASSERT_THAT(child = clone(Submitter, bp, CLONE_VM | SIGCHLD,
reinterpret_cast<void*>(this)),
SyscallSucceeds());
// Get the reply.
struct io_event events[1];
ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1));
// Verify that it is as expected.
EXPECT_EQ(events[0].data, 0x123);
EXPECT_EQ(events[0].res, strlen(kData));
// Verify that the file contains the contents.
char verify_buf[32] = {};
ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)),
SyscallSucceeds());
EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0);
int status;
ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0),
SyscallSucceedsWithValue(child));
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
<< " status " << status;
}
// Tests that AIO context can be remapped to a different address.
TEST_F(AIOTest, Mremap) {
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
struct iocb cb = CreateCallback();
struct iocb* cbs[1] = {&cb};
// Reserve address space for the mremap target so we have something safe to
// map over.
//
// N.B. We reserve 2 pages because we'll attempt to remap to 2 pages below.
// That should fail with EFAULT, but will fail with EINVAL if this mmap
// returns the page immediately below ctx_, as
// [new_address, new_address+2*kPageSize) overlaps [ctx_, ctx_+kPageSize).
void* new_address = mmap(nullptr, 2 * kPageSize, PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_THAT(reinterpret_cast<intptr_t>(new_address), SyscallSucceeds());
auto mmap_cleanup = Cleanup([new_address] {
EXPECT_THAT(munmap(new_address, 2 * kPageSize), SyscallSucceeds());
});
// Test that remapping to a larger address fails.
void* res = mremap(reinterpret_cast<void*>(ctx_), kPageSize, 2 * kPageSize,
MREMAP_FIXED | MREMAP_MAYMOVE, new_address);
ASSERT_THAT(reinterpret_cast<intptr_t>(res), SyscallFailsWithErrno(EFAULT));
// Remap context 'handle' to a different address.
res = mremap(reinterpret_cast<void*>(ctx_), kPageSize, kPageSize,
MREMAP_FIXED | MREMAP_MAYMOVE, new_address);
ASSERT_THAT(
reinterpret_cast<intptr_t>(res),
SyscallSucceedsWithValue(reinterpret_cast<intptr_t>(new_address)));
mmap_cleanup.Release();
aio_context_t old_ctx = ctx_;
ctx_ = reinterpret_cast<aio_context_t>(new_address);
// Check that submitting the request with the old 'ctx_' fails.
ASSERT_THAT(SubmitCtx(old_ctx, 1, cbs), SyscallFailsWithErrno(EINVAL));
// Submit the request with the new 'ctx_'.
ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1));
// Remap again.
new_address =
mmap(nullptr, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_THAT(reinterpret_cast<int64_t>(new_address), SyscallSucceeds());
auto mmap_cleanup2 = Cleanup([new_address] {
EXPECT_THAT(munmap(new_address, kPageSize), SyscallSucceeds());
});
res = mremap(reinterpret_cast<void*>(ctx_), kPageSize, kPageSize,
MREMAP_FIXED | MREMAP_MAYMOVE, new_address);
ASSERT_THAT(reinterpret_cast<int64_t>(res),
SyscallSucceedsWithValue(reinterpret_cast<int64_t>(new_address)));
mmap_cleanup2.Release();
ctx_ = reinterpret_cast<aio_context_t>(new_address);
// Get the reply with yet another 'ctx_' and verify it.
struct io_event events[1];
ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1));
EXPECT_EQ(events[0].data, 0x123);
EXPECT_EQ(events[0].obj, reinterpret_cast<long>(&cb));
EXPECT_EQ(events[0].res, strlen(kData));
// Verify that the file contains the contents.
char verify_buf[32] = {};
ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)),
SyscallSucceeds());
EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0);
}
// Tests that AIO context can be replaced with a different mapping at the same
// address and continue working. Don't ask why, but Linux allows it.
TEST_F(AIOTest, MremapOver) {
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
struct iocb cb = CreateCallback();
struct iocb* cbs[1] = {&cb};
ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1));
// Allocate a new VMA, copy 'ctx_' content over, and remap it on top
// of 'ctx_'.
void* new_address = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_THAT(reinterpret_cast<int64_t>(new_address), SyscallSucceeds());
auto mmap_cleanup = Cleanup([new_address] {
EXPECT_THAT(munmap(new_address, kPageSize), SyscallSucceeds());
});
memcpy(new_address, reinterpret_cast<void*>(ctx_), kPageSize);
void* res =
mremap(new_address, kPageSize, kPageSize, MREMAP_FIXED | MREMAP_MAYMOVE,
reinterpret_cast<void*>(ctx_));
ASSERT_THAT(reinterpret_cast<int64_t>(res), SyscallSucceedsWithValue(ctx_));
mmap_cleanup.Release();
// Everything continues to work just fine.
struct io_event events[1];
ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1));
EXPECT_EQ(events[0].data, 0x123);
EXPECT_EQ(events[0].obj, reinterpret_cast<long>(&cb));
EXPECT_EQ(events[0].res, strlen(kData));
// Verify that the file contains the contents.
char verify_buf[32] = {};
ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)),
SyscallSucceeds());
EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0);
}
// Tests that AIO calls fail if context's address is inaccessible.
TEST_F(AIOTest, Mprotect) {
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
struct iocb cb = CreateCallback();
struct iocb* cbs[1] = {&cb};
ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1));
// Makes the context 'handle' inaccessible and check that all subsequent
// calls fail.
ASSERT_THAT(mprotect(reinterpret_cast<void*>(ctx_), kPageSize, PROT_NONE),
SyscallSucceeds());
struct io_event events[1];
EXPECT_THAT(GetEvents(1, 1, events, nullptr), SyscallFailsWithErrno(EINVAL));
ASSERT_THAT(Submit(1, cbs), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(DestroyContext(), SyscallFailsWithErrno(EINVAL));
// Prevent TearDown from attempting to destroy the context and fail.
ctx_ = 0;
}
TEST_F(AIOTest, Timeout) {
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
struct timespec timeout;
timeout.tv_sec = 0;
timeout.tv_nsec = 10;
struct io_event events[1];
ASSERT_THAT(GetEvents(1, 1, events, &timeout), SyscallSucceedsWithValue(0));
}
class AIOReadWriteParamTest : public AIOTest,
public ::testing::WithParamInterface<int> {};
TEST_P(AIOReadWriteParamTest, BadOffset) {
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
struct iocb cb = CreateCallback();
struct iocb* cbs[1] = {&cb};
// Create a buffer that we can write to.
char buf[] = "hello world!";
cb.aio_buf = reinterpret_cast<uint64_t>(buf);
// Set the operation on the callback and give a negative offset.
const int opcode = GetParam();
cb.aio_lio_opcode = opcode;
iovec iov = {};
if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) {
// Create a valid iovec and set it in the callback.
iov.iov_base = reinterpret_cast<void*>(buf);
iov.iov_len = 1;
cb.aio_buf = reinterpret_cast<uint64_t>(&iov);
// aio_nbytes is the number of iovecs.
cb.aio_nbytes = 1;
}
// Pass a negative offset.
cb.aio_offset = -1;
// Should get error on submission.
ASSERT_THAT(Submit(1, cbs), SyscallFailsWithErrno(EINVAL));
}
INSTANTIATE_TEST_CASE_P(BadOffset, AIOReadWriteParamTest,
::testing::Values(IOCB_CMD_PREAD, IOCB_CMD_PWRITE,
IOCB_CMD_PREADV, IOCB_CMD_PWRITEV));
class AIOVectorizedParamTest : public AIOTest,
public ::testing::WithParamInterface<int> {};
TEST_P(AIOVectorizedParamTest, BadIOVecs) {
// Setup a context that is 128 entries deep.
ASSERT_THAT(SetupContext(128), SyscallSucceeds());
struct iocb cb = CreateCallback();
struct iocb* cbs[1] = {&cb};
// Modify the callback to use the operation from the param.
cb.aio_lio_opcode = GetParam();
// Create an iovec with address in kernel range, and pass that as the buffer.
iovec iov = {};
iov.iov_base = reinterpret_cast<void*>(0xFFFFFFFF00000000);
iov.iov_len = 1;
cb.aio_buf = reinterpret_cast<uint64_t>(&iov);
// aio_nbytes is the number of iovecs.
cb.aio_nbytes = 1;
// Should get error on submission.
ASSERT_THAT(Submit(1, cbs), SyscallFailsWithErrno(EFAULT));
}
INSTANTIATE_TEST_CASE_P(BadIOVecs, AIOVectorizedParamTest,
::testing::Values(IOCB_CMD_PREADV, IOCB_CMD_PWRITEV));
} // namespace testing
} // namespace gvisor
+193
View File
@@ -0,0 +1,193 @@
// Copyright 2018 Google LLC
//
// 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 <signal.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/util/file_descriptor.h"
#include "test/util/logging.h"
#include "test/util/signal_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
namespace gvisor {
namespace testing {
namespace {
// N.B. Below, main blocks SIGALRM. Test cases must unblock it if they want
// delivery.
void do_nothing_handler(int sig, siginfo_t* siginfo, void* arg) {}
// No random save as the test relies on alarm timing. Cooperative save tests
// already cover the save between alarm and read.
TEST(AlarmTest, Interrupt_NoRandomSave) {
int pipe_fds[2];
ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds());
FileDescriptor read_fd(pipe_fds[0]);
FileDescriptor write_fd(pipe_fds[1]);
// Use a signal handler that interrupts but does nothing rather than using the
// default terminate action.
struct sigaction sa;
sa.sa_sigaction = do_nothing_handler;
sigfillset(&sa.sa_mask);
sa.sa_flags = 0;
auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa));
// Actually allow SIGALRM delivery.
auto mask_cleanup =
ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM));
// Alarm in 20 second, which should be well after read blocks below.
ASSERT_THAT(alarm(20), SyscallSucceeds());
char buf;
ASSERT_THAT(read(read_fd.get(), &buf, 1), SyscallFailsWithErrno(EINTR));
}
/* Count of the number of SIGALARMS handled. */
static volatile int alarms_received = 0;
void inc_alarms_handler(int sig, siginfo_t* siginfo, void* arg) {
alarms_received++;
}
// No random save as the test relies on alarm timing. Cooperative save tests
// already cover the save between alarm and read.
TEST(AlarmTest, Restart_NoRandomSave) {
alarms_received = 0;
int pipe_fds[2];
ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds());
FileDescriptor read_fd(pipe_fds[0]);
// Write end closed by thread below.
struct sigaction sa;
sa.sa_sigaction = inc_alarms_handler;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa));
// Spawn a thread to eventually unblock the read below.
ScopedThread t([pipe_fds] {
absl::SleepFor(absl::Seconds(30));
EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds());
});
// Actually allow SIGALRM delivery.
auto mask_cleanup =
ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM));
// Alarm in 20 second, which should be well after read blocks below, but
// before it returns.
ASSERT_THAT(alarm(20), SyscallSucceeds());
// Read and eventually get an EOF from the writer closing. If SA_RESTART
// didn't work, then the alarm would not have fired and we wouldn't increment
// our alarms_received count in our signal handler, or we would have not
// restarted the syscall gracefully, which we expect below in order to be
// able to get the final EOF on the pipe.
char buf;
ASSERT_THAT(read(read_fd.get(), &buf, 1), SyscallSucceeds());
EXPECT_EQ(alarms_received, 1);
t.Join();
}
// No random save as the test relies on alarm timing. Cooperative save tests
// already cover the save between alarm and pause.
TEST(AlarmTest, SaSiginfo_NoRandomSave) {
// Use a signal handler that interrupts but does nothing rather than using the
// default terminate action.
struct sigaction sa;
sa.sa_sigaction = do_nothing_handler;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa));
// Actually allow SIGALRM delivery.
auto mask_cleanup =
ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM));
// Alarm in 20 second, which should be well after pause blocks below.
ASSERT_THAT(alarm(20), SyscallSucceeds());
ASSERT_THAT(pause(), SyscallFailsWithErrno(EINTR));
}
// No random save as the test relies on alarm timing. Cooperative save tests
// already cover the save between alarm and pause.
TEST(AlarmTest, SaInterrupt_NoRandomSave) {
// Use a signal handler that interrupts but does nothing rather than using the
// default terminate action.
struct sigaction sa;
sa.sa_sigaction = do_nothing_handler;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_INTERRUPT;
auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa));
// Actually allow SIGALRM delivery.
auto mask_cleanup =
ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM));
// Alarm in 20 second, which should be well after pause blocks below.
ASSERT_THAT(alarm(20), SyscallSucceeds());
ASSERT_THAT(pause(), SyscallFailsWithErrno(EINTR));
}
TEST(AlarmTest, UserModeSpinning) {
alarms_received = 0;
struct sigaction sa = {};
sa.sa_sigaction = inc_alarms_handler;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa));
// Actually allow SIGALRM delivery.
auto mask_cleanup =
ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM));
// Alarm in 20 second, which should be well into the loop below.
ASSERT_THAT(alarm(20), SyscallSucceeds());
// Make sure that the signal gets delivered even if we are spinning in user
// mode when it arrives.
while (!alarms_received) {
}
}
} // namespace
} // namespace testing
} // namespace gvisor
int main(int argc, char** argv) {
// These tests depend on delivering SIGALRM to the main thread. Block SIGALRM
// so that any other threads created by TestInit will also have SIGALRM
// blocked.
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0);
gvisor::testing::TestInit(&argc, &argv);
return RUN_ALL_TESTS();
}
+48
View File
@@ -0,0 +1,48 @@
// Copyright 2018 Google LLC
//
// 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 <asm/prctl.h>
#include <sys/prctl.h>
#include "gtest/gtest.h"
#include "test/util/test_util.h"
// glibc does not provide a prototype for arch_prctl() so declare it here.
extern "C" int arch_prctl(int code, uintptr_t addr);
namespace gvisor {
namespace testing {
namespace {
TEST(ArchPrctlTest, GetSetFS) {
uintptr_t orig;
const uintptr_t kNonCanonicalFsbase = 0x4141414142424242;
// Get the original FS.base and then set it to the same value (this is
// intentional because FS.base is the TLS pointer so we cannot change it
// arbitrarily).
ASSERT_THAT(arch_prctl(ARCH_GET_FS, reinterpret_cast<uintptr_t>(&orig)),
SyscallSucceeds());
ASSERT_THAT(arch_prctl(ARCH_SET_FS, orig), SyscallSucceeds());
// Trying to set FS.base to a non-canonical value should return an error.
ASSERT_THAT(arch_prctl(ARCH_SET_FS, kNonCanonicalFsbase),
SyscallFailsWithErrno(EPERM));
}
} // namespace
} // namespace testing
} // namespace gvisor
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2018 Google LLC
//
// 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 <sys/syscall.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
namespace {
TEST(BadSyscallTest, NotImplemented) {
// get_kernel_syms is not supported in Linux > 2.6, and not implemented in
// gVisor.
EXPECT_THAT(syscall(SYS_get_kernel_syms), SyscallFailsWithErrno(ENOSYS));
}
TEST(BadSyscallTest, NegativeOne) {
EXPECT_THAT(syscall(-1), SyscallFailsWithErrno(ENOSYS));
}
} // namespace
} // namespace testing
} // namespace gvisor

Some files were not shown because too many files have changed in this diff Show More