diff options
Diffstat (limited to 'test')
68 files changed, 968 insertions, 360 deletions
diff --git a/test/BUILD b/test/BUILD index 01fa01f2e..34b950644 100644 --- a/test/BUILD +++ b/test/BUILD @@ -1,44 +1 @@ -package(licenses = ["notice"]) # Apache 2.0 - -# We need to define a bazel platform and toolchain to specify dockerPrivileged -# and dockerRunAsRoot options, they are required to run tests on the RBE -# cluster in Kokoro. -alias( - name = "rbe_ubuntu1604", - actual = ":rbe_ubuntu1604_r346485", -) - -platform( - name = "rbe_ubuntu1604_r346485", - constraint_values = [ - "@bazel_tools//platforms:x86_64", - "@bazel_tools//platforms:linux", - "@bazel_tools//tools/cpp:clang", - "@bazel_toolchains//constraints:xenial", - "@bazel_toolchains//constraints/sanitizers:support_msan", - ], - remote_execution_properties = """ - properties: { - name: "container-image" - value:"docker://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:69c9f1652941d64a46f6f7358a44c1718f25caa5cb1ced4a58ccc5281cd183b5" - } - properties: { - name: "dockerAddCapabilities" - value: "SYS_ADMIN" - } - properties: { - name: "dockerPrivileged" - value: "true" - } - """, -) - -toolchain( - name = "cc-toolchain-clang-x86_64-default", - exec_compatible_with = [ - ], - target_compatible_with = [ - ], - toolchain = "@bazel_toolchains//configs/ubuntu16_04_clang/9.0.0/bazel_0.28.0/cc:cc-compiler-k8", - toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", -) +package(licenses = ["notice"]) diff --git a/test/e2e/BUILD b/test/e2e/BUILD index 4fe03a220..76e04f878 100644 --- a/test/e2e/BUILD +++ b/test/e2e/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("//tools:defs.bzl", "go_library", "go_test") package(licenses = ["notice"]) @@ -10,7 +10,7 @@ go_test( "integration_test.go", "regression_test.go", ], - embed = [":integration"], + library = ":integration", tags = [ # Requires docker and runsc to be configured before the test runs. "manual", @@ -29,5 +29,4 @@ go_test( go_library( name = "integration", srcs = ["integration.go"], - importpath = "gvisor.dev/gvisor/test/integration", ) diff --git a/test/image/BUILD b/test/image/BUILD index 09b0a0ad5..7392ac54e 100644 --- a/test/image/BUILD +++ b/test/image/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("//tools:defs.bzl", "go_library", "go_test") package(licenses = ["notice"]) @@ -14,7 +14,7 @@ go_test( "ruby.rb", "ruby.sh", ], - embed = [":image"], + library = ":image", tags = [ # Requires docker and runsc to be configured before the test runs. "manual", @@ -30,5 +30,4 @@ go_test( go_library( name = "image", srcs = ["image.go"], - importpath = "gvisor.dev/gvisor/test/image", ) diff --git a/test/iptables/BUILD b/test/iptables/BUILD index 372ba7abf..6bb3b82b5 100644 --- a/test/iptables/BUILD +++ b/test/iptables/BUILD @@ -1,9 +1,10 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("//tools:defs.bzl", "go_library", "go_test") package(licenses = ["notice"]) go_library( name = "iptables", + testonly = 1, srcs = [ "filter_input.go", "filter_output.go", @@ -11,8 +12,10 @@ go_library( "iptables_util.go", "nat.go", ], - importpath = "gvisor.dev/gvisor/test/iptables", visibility = ["//test/iptables:__subpackages__"], + deps = [ + "//runsc/testutil", + ], ) go_test( @@ -20,7 +23,7 @@ go_test( srcs = [ "iptables_test.go", ], - embed = [":iptables"], + library = ":iptables", tags = [ "local", "manual", diff --git a/test/iptables/README.md b/test/iptables/README.md index 9f8e34420..8f61b4c41 100644 --- a/test/iptables/README.md +++ b/test/iptables/README.md @@ -28,7 +28,7 @@ Your test is now runnable with bazel! Build the testing Docker container: ```bash -$ bazel run //test/iptables/runner -- --norun +$ bazel run //test/iptables/runner-image -- --norun ``` Run an individual test via: diff --git a/test/iptables/filter_input.go b/test/iptables/filter_input.go index 03e4a1d72..fd02ff2ff 100644 --- a/test/iptables/filter_input.go +++ b/test/iptables/filter_input.go @@ -30,6 +30,7 @@ const ( func init() { RegisterTestCase(FilterInputDropAll{}) RegisterTestCase(FilterInputDropDifferentUDPPort{}) + RegisterTestCase(FilterInputDropOnlyUDP{}) RegisterTestCase(FilterInputDropTCPDestPort{}) RegisterTestCase(FilterInputDropTCPSrcPort{}) RegisterTestCase(FilterInputDropUDPPort{}) @@ -67,6 +68,35 @@ func (FilterInputDropUDP) LocalAction(ip net.IP) error { return sendUDPLoop(ip, dropPort, sendloopDuration) } +// FilterInputDropOnlyUDP tests that "-p udp -j DROP" only affects UDP traffic. +type FilterInputDropOnlyUDP struct{} + +// Name implements TestCase.Name. +func (FilterInputDropOnlyUDP) Name() string { + return "FilterInputDropOnlyUDP" +} + +// ContainerAction implements TestCase.ContainerAction. +func (FilterInputDropOnlyUDP) ContainerAction(ip net.IP) error { + if err := filterTable("-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil { + return err + } + + // Listen for a TCP connection, which should be allowed. + if err := listenTCP(acceptPort, sendloopDuration); err != nil { + return fmt.Errorf("failed to establish a connection %v", err) + } + + return nil +} + +// LocalAction implements TestCase.LocalAction. +func (FilterInputDropOnlyUDP) LocalAction(ip net.IP) error { + // Try to establish a TCP connection with the container, which should + // succeed. + return connectTCP(ip, acceptPort, dropPort, sendloopDuration) +} + // FilterInputDropUDPPort tests that we can drop UDP traffic by port. type FilterInputDropUDPPort struct{} diff --git a/test/iptables/iptables_test.go b/test/iptables/iptables_test.go index 1cda10365..41909582a 100644 --- a/test/iptables/iptables_test.go +++ b/test/iptables/iptables_test.go @@ -15,6 +15,7 @@ package iptables import ( + "flag" "fmt" "net" "os" @@ -22,7 +23,6 @@ import ( "testing" "time" - "flag" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/dockerutil" "gvisor.dev/gvisor/runsc/testutil" @@ -30,7 +30,7 @@ import ( const timeout = 18 * time.Second -var image = flag.String("image", "bazel/test/iptables/runner:runner", "image to run tests in") +var image = flag.String("image", "bazel/test/iptables/runner:runner-image", "image to run tests in") type result struct { output string @@ -184,6 +184,12 @@ func TestFilterInputDropAll(t *testing.T) { } } +func TestFilterInputDropOnlyUDP(t *testing.T) { + if err := singleTest(FilterInputDropOnlyUDP{}); err != nil { + t.Fatal(err) + } +} + func TestNATRedirectUDPPort(t *testing.T) { if err := singleTest(NATRedirectUDPPort{}); err != nil { t.Fatal(err) diff --git a/test/iptables/iptables_util.go b/test/iptables/iptables_util.go index 1c4f4f665..043114c78 100644 --- a/test/iptables/iptables_util.go +++ b/test/iptables/iptables_util.go @@ -19,6 +19,8 @@ import ( "net" "os/exec" "time" + + "gvisor.dev/gvisor/runsc/testutil" ) const iptablesBinary = "iptables" @@ -105,31 +107,26 @@ func listenTCP(port int, timeout time.Duration) error { } // connectTCP connects the TCP server over specified local port, server IP and remote/server port. -func connectTCP(ip net.IP, remotePort, localPort int, duration time.Duration) error { - remote := net.TCPAddr{ +func connectTCP(ip net.IP, remotePort, localPort int, timeout time.Duration) error { + contAddr := net.TCPAddr{ IP: ip, Port: remotePort, } - - local := net.TCPAddr{ - Port: localPort, - } - - // Container may not be up. Retry DialTCP over a duration. - to := time.After(duration) - for { - conn, err := net.DialTCP("tcp4", &local, &remote) - if err == nil { - conn.Close() - return nil + // The container may not be listening when we first connect, so retry + // upon error. + callback := func() error { + localAddr := net.TCPAddr{ + Port: localPort, } - select { - // Timed out waiting for connection to be accepted. - case <-to: - return err - default: - time.Sleep(200 * time.Millisecond) + conn, err := net.DialTCP("tcp4", &localAddr, &contAddr) + if conn != nil { + conn.Close() } + return err } - return fmt.Errorf("Failed to establish connection on port %d", localPort) + if err := testutil.Poll(callback, timeout); err != nil { + return fmt.Errorf("timed out waiting to send IP, most recent error: %v", err) + } + + return nil } diff --git a/test/iptables/runner/BUILD b/test/iptables/runner/BUILD index c6c42d870..b9199387a 100644 --- a/test/iptables/runner/BUILD +++ b/test/iptables/runner/BUILD @@ -1,15 +1,22 @@ -load("@io_bazel_rules_docker//go:image.bzl", "go_image") -load("@io_bazel_rules_docker//container:container.bzl", "container_image") +load("//tools:defs.bzl", "container_image", "go_binary", "go_image") package(licenses = ["notice"]) +go_binary( + name = "runner", + testonly = 1, + srcs = ["main.go"], + deps = ["//test/iptables"], +) + container_image( name = "iptables-base", base = "@iptables-test//image", ) go_image( - name = "runner", + name = "runner-image", + testonly = 1, srcs = ["main.go"], base = ":iptables-base", deps = ["//test/iptables"], diff --git a/test/root/BUILD b/test/root/BUILD index d5dd9bca2..23ce2a70f 100644 --- a/test/root/BUILD +++ b/test/root/BUILD @@ -1,11 +1,10 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("//tools:defs.bzl", "go_library", "go_test") package(licenses = ["notice"]) go_library( name = "root", srcs = ["root.go"], - importpath = "gvisor.dev/gvisor/test/root", ) go_test( @@ -21,7 +20,7 @@ go_test( data = [ "//runsc", ], - embed = [":root"], + library = ":root", tags = [ # Requires docker and runsc to be configured before the test runs. # Also test only runs as root. diff --git a/test/root/testdata/BUILD b/test/root/testdata/BUILD index 125633680..bca5f9cab 100644 --- a/test/root/testdata/BUILD +++ b/test/root/testdata/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//tools:defs.bzl", "go_library") package(licenses = ["notice"]) @@ -12,7 +12,6 @@ go_library( "sandbox.go", "simple.go", ], - importpath = "gvisor.dev/gvisor/test/root/testdata", visibility = [ "//visibility:public", ], diff --git a/test/runtimes/BUILD b/test/runtimes/BUILD index 367295206..2c472bf8d 100644 --- a/test/runtimes/BUILD +++ b/test/runtimes/BUILD @@ -1,6 +1,6 @@ # These packages are used to run language runtime tests inside gVisor sandboxes. -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test") +load("//tools:defs.bzl", "go_binary", "go_test") load("//test/runtimes:build_defs.bzl", "runtime_test") package(licenses = ["notice"]) @@ -49,5 +49,5 @@ go_test( name = "blacklist_test", size = "small", srcs = ["blacklist_test.go"], - embed = [":runner"], + library = ":runner", ) diff --git a/test/runtimes/build_defs.bzl b/test/runtimes/build_defs.bzl index 6f84ca852..92e275a76 100644 --- a/test/runtimes/build_defs.bzl +++ b/test/runtimes/build_defs.bzl @@ -1,6 +1,6 @@ """Defines a rule for runtime test targets.""" -load("@io_bazel_rules_go//go:def.bzl", "go_test") +load("//tools:defs.bzl", "go_test", "loopback") def runtime_test( name, @@ -34,6 +34,7 @@ def runtime_test( ] data = [ ":runner", + loopback, ] if blacklist_file: args += ["--blacklist_file", "test/runtimes/" + blacklist_file] @@ -61,7 +62,7 @@ def blacklist_test(name, blacklist_file): """Test that a blacklist parses correctly.""" go_test( name = name + "_blacklist_test", - embed = [":runner"], + library = ":runner", srcs = ["blacklist_test.go"], args = ["--blacklist_file", "test/runtimes/" + blacklist_file], data = [blacklist_file], diff --git a/test/runtimes/images/proctor/BUILD b/test/runtimes/images/proctor/BUILD index 09dc6c42f..85e004c45 100644 --- a/test/runtimes/images/proctor/BUILD +++ b/test/runtimes/images/proctor/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test") +load("//tools:defs.bzl", "go_binary", "go_test") package(licenses = ["notice"]) @@ -19,7 +19,7 @@ go_test( name = "proctor_test", size = "small", srcs = ["proctor_test.go"], - embed = [":proctor"], + library = ":proctor", deps = [ "//runsc/testutil", ], diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 90d52e73b..40e974314 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary") +load("//tools:defs.bzl", "go_binary") load("//test/syscalls:build_defs.bzl", "syscall_test") package(licenses = ["notice"]) diff --git a/test/syscalls/build_defs.bzl b/test/syscalls/build_defs.bzl index aaf77c65b..1df761dd0 100644 --- a/test/syscalls/build_defs.bzl +++ b/test/syscalls/build_defs.bzl @@ -1,5 +1,7 @@ """Defines a rule for syscall test targets.""" +load("//tools:defs.bzl", "loopback") + # syscall_test is a macro that will create targets to run the given test target # on the host (native) and runsc. def syscall_test( @@ -135,6 +137,7 @@ def _syscall_test( name = name, data = [ ":syscall_test_runner", + loopback, test, ], args = args, @@ -148,6 +151,3 @@ def sh_test(**kwargs): native.sh_test( **kwargs ) - -def select_for_linux(for_linux, for_others = []): - return for_linux diff --git a/test/syscalls/gtest/BUILD b/test/syscalls/gtest/BUILD index 9293f25cb..de4b2727c 100644 --- a/test/syscalls/gtest/BUILD +++ b/test/syscalls/gtest/BUILD @@ -1,12 +1,9 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//tools:defs.bzl", "go_library") package(licenses = ["notice"]) go_library( name = "gtest", srcs = ["gtest.go"], - importpath = "gvisor.dev/gvisor/test/syscalls/gtest", - visibility = [ - "//test:__subpackages__", - ], + visibility = ["//:sandbox"], ) diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc index a7cbee06b..2751fb4e7 100644 --- a/test/syscalls/linux/32bit.cc +++ b/test/syscalls/linux/32bit.cc @@ -15,10 +15,12 @@ #include <string.h> #include <sys/mman.h> +#include "gtest/gtest.h" +#include "absl/base/macros.h" #include "test/util/memory_util.h" +#include "test/util/platform_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." @@ -30,7 +32,6 @@ 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'}; @@ -43,6 +44,7 @@ void ExitGroup32(const char instruction[2], int code) { // Fill with INT 3 in case we execute too far. memset(m.ptr(), kInt3, m.len()); + // Copy in the actual instruction. memcpy(m.ptr(), instruction, 2); // We're playing *extremely* fast-and-loose with the various syscall ABIs @@ -71,77 +73,94 @@ void ExitGroup32(const char instruction[2], int code) { "iretl\n" "int $3\n" : - : [code] "m"(code), [ip] "d"(m.ptr()) + : [ code ] "m"(code), [ ip ] "d"(m.ptr()) : "rax", "rbx", "rsp"); } constexpr int kExitCode = 42; TEST(Syscall32Bit, Int80) { - switch (GvisorPlatform()) { - case Platform::kKVM: - // TODO(b/111805002): 32-bit segments are broken (but not explictly - // disabled). - return; - case Platform::kPtrace: - // TODO(gvisor.dev/issue/167): The ptrace platform does not have a - // consistent story here. - return; - case Platform::kNative: + switch (PlatformSupport32Bit()) { + case PlatformSupport::NotSupported: + break; + case PlatformSupport::Segfault: + EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), + ::testing::KilledBySignal(SIGSEGV), ""); break; - } - // Upstream Linux. 32-bit syscalls allowed. - EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42), - ""); -} + case PlatformSupport::Ignored: + // Since the call is ignored, we'll hit the int3 trap. + EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), + ::testing::KilledBySignal(SIGTRAP), ""); + break; -TEST(Syscall32Bit, Sysenter) { - switch (GvisorPlatform()) { - case Platform::kKVM: - // TODO(b/111805002): See above. - return; - case Platform::kPtrace: - // TODO(gvisor.dev/issue/167): See above. - return; - case Platform::kNative: + case PlatformSupport::Allowed: + EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42), + ""); break; } +} - if (GetCPUVendor() == CPUVendor::kAMD) { +TEST(Syscall32Bit, Sysenter) { + if (PlatformSupport32Bit() == PlatformSupport::Allowed && + 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), - ""); -} + switch (PlatformSupport32Bit()) { + case PlatformSupport::NotSupported: + break; -TEST(Syscall32Bit, Syscall) { - switch (GvisorPlatform()) { - case Platform::kKVM: - // TODO(b/111805002): See above. - return; - case Platform::kPtrace: - // TODO(gvisor.dev/issue/167): See above. - return; - case Platform::kNative: + case PlatformSupport::Segfault: + EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), + ::testing::KilledBySignal(SIGSEGV), ""); + break; + + case PlatformSupport::Ignored: + // See above, except expected code is SIGSEGV. + EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), + ::testing::KilledBySignal(SIGSEGV), ""); + break; + + case PlatformSupport::Allowed: + EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), + ::testing::ExitedWithCode(42), ""); break; } +} - if (GetCPUVendor() == CPUVendor::kIntel) { +TEST(Syscall32Bit, Syscall) { + if (PlatformSupport32Bit() == PlatformSupport::Allowed && + 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), - ""); + switch (PlatformSupport32Bit()) { + case PlatformSupport::NotSupported: + break; + + case PlatformSupport::Segfault: + EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), + ::testing::KilledBySignal(SIGSEGV), ""); + break; + + case PlatformSupport::Ignored: + // See above. + EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), + ::testing::KilledBySignal(SIGILL), ""); + break; + + case PlatformSupport::Allowed: + EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), + ::testing::ExitedWithCode(42), ""); + break; + } } // Far call code called below. @@ -205,19 +224,20 @@ void FarCall32() { } TEST(Call32Bit, Disallowed) { - switch (GvisorPlatform()) { - case Platform::kKVM: - // TODO(b/111805002): See above. - return; - case Platform::kPtrace: - // The ptrace platform cannot prevent switching to compatibility mode. - ABSL_FALLTHROUGH_INTENDED; - case Platform::kNative: + switch (PlatformSupport32Bit()) { + case PlatformSupport::NotSupported: break; - } - // Shouldn't crash. - FarCall32(); + case PlatformSupport::Segfault: + EXPECT_EXIT(FarCall32(), ::testing::KilledBySignal(SIGSEGV), ""); + break; + + case PlatformSupport::Ignored: + ABSL_FALLTHROUGH_INTENDED; + case PlatformSupport::Allowed: + // Shouldn't crash. + FarCall32(); + } } } // namespace diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 4c7ec3f06..6f57c9755 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1,5 +1,4 @@ -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") -load("//test/syscalls:build_defs.bzl", "select_for_linux") +load("//tools:defs.bzl", "cc_binary", "cc_library", "default_net_util", "select_system") package( default_visibility = ["//:sandbox"], @@ -126,13 +125,11 @@ cc_library( testonly = 1, srcs = [ "socket_test_util.cc", - ] + select_for_linux( - [ - "socket_test_util_impl.cc", - ], - ), + "socket_test_util_impl.cc", + ], hdrs = ["socket_test_util.h"], - deps = [ + defines = select_system(), + deps = default_net_util() + [ "@com_google_googletest//:gtest", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", @@ -143,8 +140,7 @@ cc_library( "//test/util:temp_path", "//test/util:test_util", "//test/util:thread_util", - ] + select_for_linux([ - ]), + ], ) cc_library( @@ -201,9 +197,11 @@ cc_binary( linkstatic = 1, deps = [ "//test/util:memory_util", + "//test/util:platform_util", "//test/util:posix_error", "//test/util:test_main", "//test/util:test_util", + "@com_google_absl//absl/base:core_headers", "@com_google_googletest//:gtest", ], ) @@ -483,6 +481,7 @@ cc_binary( srcs = ["concurrency.cc"], linkstatic = 1, deps = [ + "//test/util:platform_util", "//test/util:test_main", "//test/util:test_util", "//test/util:thread_util", @@ -588,6 +587,7 @@ cc_binary( linkstatic = 1, deps = [ "//test/util:logging", + "//test/util:platform_util", "//test/util:signal_util", "//test/util:test_main", "//test/util:test_util", @@ -754,6 +754,7 @@ cc_binary( ":socket_test_util", "//test/util:cleanup", "//test/util:eventfd_util", + "//test/util:fs_util", "//test/util:multiprocess_util", "//test/util:posix_error", "//test/util:save_util", @@ -1391,6 +1392,7 @@ cc_binary( linkstatic = 1, deps = [ "//test/util:file_descriptor", + "//test/util:fs_util", "//test/util:posix_error", "//test/util:temp_path", "//test/util:test_main", @@ -1443,6 +1445,7 @@ cc_binary( srcs = ["arch_prctl.cc"], linkstatic = 1, deps = [ + "//test/util:file_descriptor", "//test/util:test_main", "//test/util:test_util", "@com_google_googletest//:gtest", @@ -1661,6 +1664,7 @@ cc_binary( deps = [ "//test/util:logging", "//test/util:multiprocess_util", + "//test/util:platform_util", "//test/util:signal_util", "//test/util:test_util", "//test/util:thread_util", @@ -2171,6 +2175,7 @@ cc_library( ":socket_test_util", "//test/util:test_util", "//test/util:thread_util", + "@com_google_absl//absl/memory", "@com_google_absl//absl/time", "@com_google_googletest//:gtest", ], @@ -3383,11 +3388,11 @@ cc_library( name = "udp_socket_test_cases", testonly = 1, srcs = [ - "udp_socket_test_cases.cc", - ] + select_for_linux([ "udp_socket_errqueue_test_case.cc", - ]), + "udp_socket_test_cases.cc", + ], hdrs = ["udp_socket_test_cases.h"], + defines = select_system(), deps = [ ":socket_test_util", ":unix_domain_socket_test_util", diff --git a/test/syscalls/linux/concurrency.cc b/test/syscalls/linux/concurrency.cc index 00b96b34a..f41f99900 100644 --- a/test/syscalls/linux/concurrency.cc +++ b/test/syscalls/linux/concurrency.cc @@ -20,6 +20,7 @@ #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" +#include "test/util/platform_util.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" @@ -99,6 +100,7 @@ TEST(ConcurrencyTest, MultiProcessMultithreaded) { // Test that multiple processes can execute concurrently, even if one process // never yields. TEST(ConcurrencyTest, MultiProcessConcurrency) { + SKIP_IF(PlatformSupportMultiProcess() == PlatformSupport::NotSupported); pid_t child_pid = fork(); if (child_pid == 0) { diff --git a/test/syscalls/linux/connect_external.cc b/test/syscalls/linux/connect_external.cc index bfe1da82e..1edb50e47 100644 --- a/test/syscalls/linux/connect_external.cc +++ b/test/syscalls/linux/connect_external.cc @@ -56,7 +56,7 @@ TEST_P(GoferStreamSeqpacketTest, Echo) { ProtocolSocket proto; std::tie(env, proto) = GetParam(); - char *val = getenv(env.c_str()); + char* val = getenv(env.c_str()); ASSERT_NE(val, nullptr); std::string root(val); @@ -69,7 +69,7 @@ TEST_P(GoferStreamSeqpacketTest, Echo) { addr.sun_family = AF_UNIX; memcpy(addr.sun_path, socket_path.c_str(), socket_path.length()); - ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr *>(&addr), + ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), SyscallSucceeds()); @@ -92,7 +92,7 @@ TEST_P(GoferStreamSeqpacketTest, NonListening) { ProtocolSocket proto; std::tie(env, proto) = GetParam(); - char *val = getenv(env.c_str()); + char* val = getenv(env.c_str()); ASSERT_NE(val, nullptr); std::string root(val); @@ -105,7 +105,7 @@ TEST_P(GoferStreamSeqpacketTest, NonListening) { addr.sun_family = AF_UNIX; memcpy(addr.sun_path, socket_path.c_str(), socket_path.length()); - ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr *>(&addr), + ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), SyscallFailsWithErrno(ECONNREFUSED)); } @@ -127,7 +127,7 @@ using GoferDgramTest = ::testing::TestWithParam<std::string>; // unnamed. The server thus has no way to reply to us. TEST_P(GoferDgramTest, Null) { std::string env = GetParam(); - char *val = getenv(env.c_str()); + char* val = getenv(env.c_str()); ASSERT_NE(val, nullptr); std::string root(val); @@ -140,7 +140,7 @@ TEST_P(GoferDgramTest, Null) { addr.sun_family = AF_UNIX; memcpy(addr.sun_path, socket_path.c_str(), socket_path.length()); - ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr *>(&addr), + ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), SyscallSucceeds()); diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc index 367682c3d..927001eee 100644 --- a/test/syscalls/linux/eventfd.cc +++ b/test/syscalls/linux/eventfd.cc @@ -132,6 +132,31 @@ TEST(EventfdTest, BigWriteBigRead) { EXPECT_EQ(l[0], 1); } +TEST(EventfdTest, SpliceFromPipePartialSucceeds) { + int pipes[2]; + ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds()); + const FileDescriptor pipe_rfd(pipes[0]); + const FileDescriptor pipe_wfd(pipes[1]); + constexpr uint64_t kVal{1}; + + FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK)); + + uint64_t event_array[2]; + event_array[0] = kVal; + event_array[1] = kVal; + ASSERT_THAT(write(pipe_wfd.get(), event_array, sizeof(event_array)), + SyscallSucceedsWithValue(sizeof(event_array))); + EXPECT_THAT(splice(pipe_rfd.get(), /*__offin=*/nullptr, efd.get(), + /*__offout=*/nullptr, sizeof(event_array[0]) + 1, + SPLICE_F_NONBLOCK), + SyscallSucceedsWithValue(sizeof(event_array[0]))); + + uint64_t val; + ASSERT_THAT(read(efd.get(), &val, sizeof(val)), + SyscallSucceedsWithValue(sizeof(val))); + EXPECT_EQ(val, kVal); +} + // NotifyNonZero is inherently racy, so random save is disabled. TEST(EventfdTest, NotifyNonZero_NoRandomSave) { // Waits will time out at 10 seconds. diff --git a/test/syscalls/linux/exceptions.cc b/test/syscalls/linux/exceptions.cc index 3d564e720..420b9543f 100644 --- a/test/syscalls/linux/exceptions.cc +++ b/test/syscalls/linux/exceptions.cc @@ -16,6 +16,7 @@ #include "gtest/gtest.h" #include "test/util/logging.h" +#include "test/util/platform_util.h" #include "test/util/signal_util.h" #include "test/util/test_util.h" @@ -324,6 +325,7 @@ TEST(ExceptionTest, AlignmentHalt) { } TEST(ExceptionTest, AlignmentCheck) { + SKIP_IF(PlatformSupportAlignmentCheck() != PlatformSupport::Allowed); // See above. struct sigaction sa = {}; diff --git a/test/syscalls/linux/fault.cc b/test/syscalls/linux/fault.cc index f6e19026f..a85750382 100644 --- a/test/syscalls/linux/fault.cc +++ b/test/syscalls/linux/fault.cc @@ -37,6 +37,9 @@ int GetPcFromUcontext(ucontext_t* uc, uintptr_t* pc) { #elif defined(__i386__) *pc = uc->uc_mcontext.gregs[REG_EIP]; return 1; +#elif defined(__aarch64__) + *pc = uc->uc_mcontext.pc; + return 1; #else return 0; #endif diff --git a/test/syscalls/linux/fpsig_fork.cc b/test/syscalls/linux/fpsig_fork.cc index e7e9f06a1..a346f1f00 100644 --- a/test/syscalls/linux/fpsig_fork.cc +++ b/test/syscalls/linux/fpsig_fork.cc @@ -76,8 +76,8 @@ TEST(FPSigTest, Fork) { "movl %[sig], %%edx;" "syscall;" : - : [killnr] "i"(__NR_tgkill), [parent] "rm"(parent), - [tid] "rm"(parent_tid), [sig] "i"(SIGUSR1) + : [ killnr ] "i"(__NR_tgkill), [ parent ] "rm"(parent), + [ tid ] "rm"(parent_tid), [ sig ] "i"(SIGUSR1) : "rax", "rdi", "rsi", "rdx", // Clobbered by syscall. "rcx", "r11"); diff --git a/test/syscalls/linux/fpsig_nested.cc b/test/syscalls/linux/fpsig_nested.cc index 395463aed..c476a8e7a 100644 --- a/test/syscalls/linux/fpsig_nested.cc +++ b/test/syscalls/linux/fpsig_nested.cc @@ -61,8 +61,8 @@ void sigusr1(int s, siginfo_t* siginfo, void* _uc) { "movl %[sig], %%edx;" "syscall;" : - : [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid), - [sig] "i"(SIGUSR2) + : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid), + [ sig ] "i"(SIGUSR2) : "rax", "rdi", "rsi", "rdx", // Clobbered by syscall. "rcx", "r11"); @@ -107,8 +107,8 @@ TEST(FPSigTest, NestedSignals) { "movl %[sig], %%edx;" "syscall;" : - : [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid), - [sig] "i"(SIGUSR1) + : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid), + [ sig ] "i"(SIGUSR1) : "rax", "rdi", "rsi", "rdx", // Clobbered by syscall. "rcx", "r11"); diff --git a/test/syscalls/linux/getrusage.cc b/test/syscalls/linux/getrusage.cc index 9bdb1e4cd..0e51d42a8 100644 --- a/test/syscalls/linux/getrusage.cc +++ b/test/syscalls/linux/getrusage.cc @@ -67,7 +67,7 @@ TEST(GetrusageTest, Grandchild) { pid = fork(); if (pid == 0) { int flags = MAP_ANONYMOUS | MAP_POPULATE | MAP_PRIVATE; - void *addr = + void* addr = mmap(nullptr, kGrandchildSizeKb * 1024, PROT_WRITE, flags, -1, 0); TEST_PCHECK(addr != MAP_FAILED); } else { diff --git a/test/syscalls/linux/iptables.h b/test/syscalls/linux/iptables.h index 616bea550..0719c60a4 100644 --- a/test/syscalls/linux/iptables.h +++ b/test/syscalls/linux/iptables.h @@ -188,7 +188,7 @@ struct ipt_replace { unsigned int num_counters; // The unchanged values from each ipt_entry's counters. - struct xt_counters *counters; + struct xt_counters* counters; // The entries to write to the table. This will run past the size defined by // sizeof(srtuct ipt_replace); diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc index dd5352954..e74fa2ed5 100644 --- a/test/syscalls/linux/link.cc +++ b/test/syscalls/linux/link.cc @@ -55,7 +55,8 @@ TEST(LinkTest, CanCreateLinkFile) { const std::string newname = NewTempAbsPath(); // Get the initial link count. - uint64_t initial_link_count = ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path())); + uint64_t initial_link_count = + ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path())); EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), SyscallSucceeds()); diff --git a/test/syscalls/linux/madvise.cc b/test/syscalls/linux/madvise.cc index 7fd0ea20c..5a1973f60 100644 --- a/test/syscalls/linux/madvise.cc +++ b/test/syscalls/linux/madvise.cc @@ -139,7 +139,7 @@ TEST(MadviseDontneedTest, IgnoresPermissions) { TEST(MadviseDontforkTest, AddressLength) { auto m = ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); - char *addr = static_cast<char *>(m.ptr()); + char* addr = static_cast<char*>(m.ptr()); // Address must be page aligned. EXPECT_THAT(madvise(addr + 1, kPageSize, MADV_DONTFORK), @@ -168,9 +168,9 @@ TEST(MadviseDontforkTest, DontforkShared) { Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( nullptr, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); - const Mapping ms1 = Mapping(reinterpret_cast<void *>(m.addr()), kPageSize); + const Mapping ms1 = Mapping(reinterpret_cast<void*>(m.addr()), kPageSize); const Mapping ms2 = - Mapping(reinterpret_cast<void *>(m.addr() + kPageSize), kPageSize); + Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize); m.release(); ASSERT_THAT(madvise(ms2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds()); @@ -197,11 +197,11 @@ TEST(MadviseDontforkTest, DontforkAnonPrivate) { // Mmap three anonymous pages and MADV_DONTFORK the middle page. Mapping m = ASSERT_NO_ERRNO_AND_VALUE( MmapAnon(kPageSize * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - const Mapping mp1 = Mapping(reinterpret_cast<void *>(m.addr()), kPageSize); + const Mapping mp1 = Mapping(reinterpret_cast<void*>(m.addr()), kPageSize); const Mapping mp2 = - Mapping(reinterpret_cast<void *>(m.addr() + kPageSize), kPageSize); + Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize); const Mapping mp3 = - Mapping(reinterpret_cast<void *>(m.addr() + 2 * kPageSize), kPageSize); + Mapping(reinterpret_cast<void*>(m.addr() + 2 * kPageSize), kPageSize); m.release(); ASSERT_THAT(madvise(mp2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds()); diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc index 9d5f47651..059fad598 100644 --- a/test/syscalls/linux/mempolicy.cc +++ b/test/syscalls/linux/mempolicy.cc @@ -43,17 +43,17 @@ namespace { #define MPOL_MF_MOVE (1 << 1) #define MPOL_MF_MOVE_ALL (1 << 2) -int get_mempolicy(int *policy, uint64_t *nmask, uint64_t maxnode, void *addr, +int get_mempolicy(int* policy, uint64_t* nmask, uint64_t maxnode, void* addr, int flags) { return syscall(SYS_get_mempolicy, policy, nmask, maxnode, addr, flags); } -int set_mempolicy(int mode, uint64_t *nmask, uint64_t maxnode) { +int set_mempolicy(int mode, uint64_t* nmask, uint64_t maxnode) { return syscall(SYS_set_mempolicy, mode, nmask, maxnode); } -int mbind(void *addr, unsigned long len, int mode, - const unsigned long *nodemask, unsigned long maxnode, +int mbind(void* addr, unsigned long len, int mode, + const unsigned long* nodemask, unsigned long maxnode, unsigned flags) { return syscall(SYS_mbind, addr, len, mode, nodemask, maxnode, flags); } @@ -68,7 +68,7 @@ Cleanup ScopedMempolicy() { // Temporarily change the memory policy for the calling thread within the // caller's scope. -PosixErrorOr<Cleanup> ScopedSetMempolicy(int mode, uint64_t *nmask, +PosixErrorOr<Cleanup> ScopedSetMempolicy(int mode, uint64_t* nmask, uint64_t maxnode) { if (set_mempolicy(mode, nmask, maxnode)) { return PosixError(errno, "set_mempolicy"); diff --git a/test/syscalls/linux/mlock.cc b/test/syscalls/linux/mlock.cc index 620b4f8b4..367a90fe1 100644 --- a/test/syscalls/linux/mlock.cc +++ b/test/syscalls/linux/mlock.cc @@ -60,7 +60,6 @@ bool IsPageMlocked(uintptr_t addr) { return true; } - TEST(MlockTest, Basic) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( diff --git a/test/syscalls/linux/msync.cc b/test/syscalls/linux/msync.cc index ac7146017..2b2b6aef9 100644 --- a/test/syscalls/linux/msync.cc +++ b/test/syscalls/linux/msync.cc @@ -60,9 +60,7 @@ std::vector<std::function<PosixErrorOr<Mapping>()>> SyncableMappings() { for (int const mflags : {MAP_PRIVATE, MAP_SHARED}) { int const prot = PROT_READ | (writable ? PROT_WRITE : 0); int const oflags = O_CREAT | (writable ? O_RDWR : O_RDONLY); - funcs.push_back([=] { - return MmapAnon(kPageSize, prot, mflags); - }); + funcs.push_back([=] { return MmapAnon(kPageSize, prot, mflags); }); funcs.push_back([=]() -> PosixErrorOr<Mapping> { std::string const path = NewTempAbsPath(); ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, oflags, 0644)); diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc index 65bad06d4..3a611a86f 100644 --- a/test/syscalls/linux/proc_net.cc +++ b/test/syscalls/linux/proc_net.cc @@ -68,8 +68,8 @@ TEST(ProcSysNetIpv4Sack, CanReadAndWrite) { } PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp, - const std::string &type, - const std::string &item) { + const std::string& type, + const std::string& item) { std::vector<std::string> snmp_vec = absl::StrSplit(snmp, '\n'); // /proc/net/snmp prints a line of headers followed by a line of metrics. @@ -127,7 +127,7 @@ TEST(ProcNetSnmp, TcpReset_NoRandomSave) { }; ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1); - ASSERT_THAT(connect(s.get(), (struct sockaddr *)&sin, sizeof(sin)), + ASSERT_THAT(connect(s.get(), (struct sockaddr*)&sin, sizeof(sin)), SyscallFailsWithErrno(ECONNREFUSED)); uint64_t newAttemptFails; @@ -172,19 +172,19 @@ TEST(ProcNetSnmp, TcpEstab_NoRandomSave) { }; ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1); - ASSERT_THAT(bind(s_listen.get(), (struct sockaddr *)&sin, sizeof(sin)), + ASSERT_THAT(bind(s_listen.get(), (struct sockaddr*)&sin, sizeof(sin)), SyscallSucceeds()); ASSERT_THAT(listen(s_listen.get(), 1), SyscallSucceeds()); // Get the port bound by the listening socket. socklen_t addrlen = sizeof(sin); ASSERT_THAT( - getsockname(s_listen.get(), reinterpret_cast<sockaddr *>(&sin), &addrlen), + getsockname(s_listen.get(), reinterpret_cast<sockaddr*>(&sin), &addrlen), SyscallSucceeds()); FileDescriptor s_connect = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0)); - ASSERT_THAT(connect(s_connect.get(), (struct sockaddr *)&sin, sizeof(sin)), + ASSERT_THAT(connect(s_connect.get(), (struct sockaddr*)&sin, sizeof(sin)), SyscallSucceeds()); auto s_accept = @@ -260,7 +260,7 @@ TEST(ProcNetSnmp, UdpNoPorts_NoRandomSave) { .sin_port = htons(4444), }; ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1); - ASSERT_THAT(sendto(s.get(), "a", 1, 0, (struct sockaddr *)&sin, sizeof(sin)), + ASSERT_THAT(sendto(s.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)), SyscallSucceedsWithValue(1)); uint64_t newOutDatagrams; @@ -295,18 +295,18 @@ TEST(ProcNetSnmp, UdpIn) { .sin_port = htons(0), }; ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1); - ASSERT_THAT(bind(server.get(), (struct sockaddr *)&sin, sizeof(sin)), + ASSERT_THAT(bind(server.get(), (struct sockaddr*)&sin, sizeof(sin)), SyscallSucceeds()); // Get the port bound by the server socket. socklen_t addrlen = sizeof(sin); ASSERT_THAT( - getsockname(server.get(), reinterpret_cast<sockaddr *>(&sin), &addrlen), + getsockname(server.get(), reinterpret_cast<sockaddr*>(&sin), &addrlen), SyscallSucceeds()); FileDescriptor client = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); ASSERT_THAT( - sendto(client.get(), "a", 1, 0, (struct sockaddr *)&sin, sizeof(sin)), + sendto(client.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)), SyscallSucceedsWithValue(1)); char buf[128]; diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc index 8f3800380..4dd5cf27b 100644 --- a/test/syscalls/linux/ptrace.cc +++ b/test/syscalls/linux/ptrace.cc @@ -32,6 +32,7 @@ #include "absl/time/time.h" #include "test/util/logging.h" #include "test/util/multiprocess_util.h" +#include "test/util/platform_util.h" #include "test/util/signal_util.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" @@ -178,7 +179,8 @@ TEST(PtraceTest, GetSigMask) { // Install a signal handler for kBlockSignal to avoid termination and block // it. - TEST_PCHECK(signal(kBlockSignal, +[](int signo) {}) != SIG_ERR); + TEST_PCHECK(signal( + kBlockSignal, +[](int signo) {}) != SIG_ERR); MaybeSave(); TEST_PCHECK(sigprocmask(SIG_SETMASK, &blocked, nullptr) == 0); MaybeSave(); @@ -823,13 +825,8 @@ TEST(PtraceTest, // These tests requires knowledge of architecture-specific syscall convention. #ifdef __x86_64__ TEST(PtraceTest, Int3) { - switch (GvisorPlatform()) { - case Platform::kKVM: - // TODO(b/124248694): int3 isn't handled properly. - return; - default: - break; - } + SKIP_IF(PlatformSupportInt3() == PlatformSupport::NotSupported); + pid_t const child_pid = fork(); if (child_pid == 0) { // In child process. diff --git a/test/syscalls/linux/rseq/BUILD b/test/syscalls/linux/rseq/BUILD index 5cfe4e56f..ed488dbc2 100644 --- a/test/syscalls/linux/rseq/BUILD +++ b/test/syscalls/linux/rseq/BUILD @@ -1,8 +1,7 @@ # This package contains a standalone rseq test binary. This binary must not # depend on libc, which might use rseq itself. -load("@bazel_tools//tools/cpp:cc_flags_supplier.bzl", "cc_flags_supplier") -load("@rules_cc//cc:defs.bzl", "cc_library") +load("//tools:defs.bzl", "cc_flags_supplier", "cc_library", "cc_toolchain") package(licenses = ["notice"]) @@ -37,8 +36,8 @@ genrule( "$(location start.S)", ]), toolchains = [ + cc_toolchain, ":no_pie_cc_flags", - "@bazel_tools//tools/cpp:current_cc_toolchain", ], visibility = ["//:sandbox"], ) diff --git a/test/syscalls/linux/seccomp.cc b/test/syscalls/linux/seccomp.cc index 7e41fe7d8..294ee6808 100644 --- a/test/syscalls/linux/seccomp.cc +++ b/test/syscalls/linux/seccomp.cc @@ -113,7 +113,8 @@ TEST(SeccompTest, RetKillCausesDeathBySIGSYS) { pid_t const pid = fork(); if (pid == 0) { // Register a signal handler for SIGSYS that we don't expect to be invoked. - RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); + RegisterSignalHandler( + SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); syscall(kFilteredSyscall); TEST_CHECK_MSG(false, "Survived invocation of test syscall"); @@ -132,7 +133,8 @@ TEST(SeccompTest, RetKillOnlyKillsOneThread) { pid_t const pid = fork(); if (pid == 0) { // Register a signal handler for SIGSYS that we don't expect to be invoked. - RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); + RegisterSignalHandler( + SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); // Pass CLONE_VFORK to block the original thread in the child process until // the clone thread exits with SIGSYS. @@ -346,7 +348,8 @@ TEST(SeccompTest, LeastPermissiveFilterReturnValueApplies) { // one that causes the kill that should be ignored. pid_t const pid = fork(); if (pid == 0) { - RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); + RegisterSignalHandler( + SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRACE); ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM); diff --git a/test/syscalls/linux/sendfile_socket.cc b/test/syscalls/linux/sendfile_socket.cc index 3331288b7..8f7ee4163 100644 --- a/test/syscalls/linux/sendfile_socket.cc +++ b/test/syscalls/linux/sendfile_socket.cc @@ -41,15 +41,15 @@ class SendFileTest : public ::testing::TestWithParam<int> { struct sockaddr server_addr = {}; switch (family) { case AF_INET: { - struct sockaddr_in *server_addr_in = - reinterpret_cast<struct sockaddr_in *>(&server_addr); + struct sockaddr_in* server_addr_in = + reinterpret_cast<struct sockaddr_in*>(&server_addr); server_addr_in->sin_family = family; server_addr_in->sin_addr.s_addr = INADDR_ANY; break; } case AF_UNIX: { - struct sockaddr_un *server_addr_un = - reinterpret_cast<struct sockaddr_un *>(&server_addr); + struct sockaddr_un* server_addr_un = + reinterpret_cast<struct sockaddr_un*>(&server_addr); server_addr_un->sun_family = family; server_addr_un->sun_path[0] = '\0'; break; diff --git a/test/syscalls/linux/sigaction.cc b/test/syscalls/linux/sigaction.cc index 9a53fd3e0..9d9dd57a8 100644 --- a/test/syscalls/linux/sigaction.cc +++ b/test/syscalls/linux/sigaction.cc @@ -13,6 +13,7 @@ // limitations under the License. #include <signal.h> +#include <sys/syscall.h> #include "gtest/gtest.h" #include "test/util/test_util.h" @@ -23,45 +24,53 @@ namespace testing { namespace { TEST(SigactionTest, GetLessThanOrEqualToZeroFails) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - ASSERT_THAT(sigaction(-1, NULL, &act), SyscallFailsWithErrno(EINVAL)); - ASSERT_THAT(sigaction(0, NULL, &act), SyscallFailsWithErrno(EINVAL)); + struct sigaction act = {}; + ASSERT_THAT(sigaction(-1, nullptr, &act), SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT(sigaction(0, nullptr, &act), SyscallFailsWithErrno(EINVAL)); } TEST(SigactionTest, SetLessThanOrEqualToZeroFails) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - ASSERT_THAT(sigaction(0, &act, NULL), SyscallFailsWithErrno(EINVAL)); - ASSERT_THAT(sigaction(0, &act, NULL), SyscallFailsWithErrno(EINVAL)); + struct sigaction act = {}; + ASSERT_THAT(sigaction(0, &act, nullptr), SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT(sigaction(0, &act, nullptr), SyscallFailsWithErrno(EINVAL)); } TEST(SigactionTest, GetGreaterThanMaxFails) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - ASSERT_THAT(sigaction(SIGRTMAX + 1, NULL, &act), + struct sigaction act = {}; + ASSERT_THAT(sigaction(SIGRTMAX + 1, nullptr, &act), SyscallFailsWithErrno(EINVAL)); } TEST(SigactionTest, SetGreaterThanMaxFails) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - ASSERT_THAT(sigaction(SIGRTMAX + 1, &act, NULL), + struct sigaction act = {}; + ASSERT_THAT(sigaction(SIGRTMAX + 1, &act, nullptr), SyscallFailsWithErrno(EINVAL)); } TEST(SigactionTest, SetSigkillFails) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - ASSERT_THAT(sigaction(SIGKILL, NULL, &act), SyscallSucceeds()); - ASSERT_THAT(sigaction(SIGKILL, &act, NULL), SyscallFailsWithErrno(EINVAL)); + struct sigaction act = {}; + ASSERT_THAT(sigaction(SIGKILL, nullptr, &act), SyscallSucceeds()); + ASSERT_THAT(sigaction(SIGKILL, &act, nullptr), SyscallFailsWithErrno(EINVAL)); } TEST(SigactionTest, SetSigstopFails) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - ASSERT_THAT(sigaction(SIGSTOP, NULL, &act), SyscallSucceeds()); - ASSERT_THAT(sigaction(SIGSTOP, &act, NULL), SyscallFailsWithErrno(EINVAL)); + struct sigaction act = {}; + ASSERT_THAT(sigaction(SIGSTOP, nullptr, &act), SyscallSucceeds()); + ASSERT_THAT(sigaction(SIGSTOP, &act, nullptr), SyscallFailsWithErrno(EINVAL)); +} + +TEST(SigactionTest, BadSigsetFails) { + constexpr size_t kWrongSigSetSize = 43; + + struct sigaction act = {}; + + // The syscall itself (rather than the libc wrapper) takes the sigset_t size. + ASSERT_THAT( + syscall(SYS_rt_sigaction, SIGTERM, nullptr, &act, kWrongSigSetSize), + SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT( + syscall(SYS_rt_sigaction, SIGTERM, &act, nullptr, kWrongSigSetSize), + SyscallFailsWithErrno(EINVAL)); } } // namespace diff --git a/test/syscalls/linux/sigaltstack.cc b/test/syscalls/linux/sigaltstack.cc index a778fa639..24e7c4960 100644 --- a/test/syscalls/linux/sigaltstack.cc +++ b/test/syscalls/linux/sigaltstack.cc @@ -114,7 +114,7 @@ TEST(SigaltstackTest, ResetByExecve) { volatile bool badhandler_on_sigaltstack = true; // Set by the handler. char* volatile badhandler_low_water_mark = nullptr; // Set by the handler. -volatile uint8_t badhandler_recursive_faults = 0; // Consumed by the handler. +volatile uint8_t badhandler_recursive_faults = 0; // Consumed by the handler. void badhandler(int sig, siginfo_t* siginfo, void* arg) { char stack_var = 0; @@ -168,8 +168,8 @@ TEST(SigaltstackTest, WalksOffBottom) { // Trigger a single fault. badhandler_low_water_mark = - static_cast<char*>(stack.ss_sp) + SIGSTKSZ; // Expected top. - badhandler_recursive_faults = 0; // Disable refault. + static_cast<char*>(stack.ss_sp) + SIGSTKSZ; // Expected top. + badhandler_recursive_faults = 0; // Disable refault. Fault(); EXPECT_TRUE(badhandler_on_sigaltstack); EXPECT_THAT(sigaltstack(nullptr, &stack), SyscallSucceeds()); diff --git a/test/syscalls/linux/sigiret.cc b/test/syscalls/linux/sigiret.cc index a47c781ea..4deb1ae95 100644 --- a/test/syscalls/linux/sigiret.cc +++ b/test/syscalls/linux/sigiret.cc @@ -78,8 +78,8 @@ TEST(SigIretTest, CheckRcxR11) { "1: pause; cmpl $0, %[gotvtalrm]; je 1b;" // while (!gotvtalrm); "movq %%rcx, %[rcx];" // rcx = %rcx "movq %%r11, %[r11];" // r11 = %r11 - : [ready] "=m"(ready), [rcx] "+m"(rcx), [r11] "+m"(r11) - : [gotvtalrm] "m"(gotvtalrm) + : [ ready ] "=m"(ready), [ rcx ] "+m"(rcx), [ r11 ] "+m"(r11) + : [ gotvtalrm ] "m"(gotvtalrm) : "cc", "memory", "rcx", "r11"); // If sigreturn(2) returns via 'sysret' then %rcx and %r11 will be diff --git a/test/syscalls/linux/socket_bind_to_device_sequence.cc b/test/syscalls/linux/socket_bind_to_device_sequence.cc index 34b1058a9..637d1151a 100644 --- a/test/syscalls/linux/socket_bind_to_device_sequence.cc +++ b/test/syscalls/linux/socket_bind_to_device_sequence.cc @@ -66,7 +66,7 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> { // Gets a device by device_id. If the device_id has been seen before, returns // the previously returned device. If not, finds or creates a new device. // Returns an empty string on failure. - void GetDevice(int device_id, string *device_name) { + void GetDevice(int device_id, string* device_name) { auto device = devices_.find(device_id); if (device != devices_.end()) { *device_name = device->second; @@ -112,7 +112,7 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> { // Sets the socket_id to uniquely identify the socket bound if it is not // nullptr. void BindSocket(bool reuse_port, bool reuse_addr, int device_id = 0, - int want = 0, int *socket_id = nullptr) { + int want = 0, int* socket_id = nullptr) { next_socket_id_++; sockets_to_close_[next_socket_id_] = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto socket_fd = sockets_to_close_[next_socket_id_]->get(); @@ -154,12 +154,12 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> { addr.sin_port = port_; if (want == 0) { ASSERT_THAT( - bind(socket_fd, reinterpret_cast<const struct sockaddr *>(&addr), + bind(socket_fd, reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), SyscallSucceeds()); } else { ASSERT_THAT( - bind(socket_fd, reinterpret_cast<const struct sockaddr *>(&addr), + bind(socket_fd, reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), SyscallFailsWithErrno(want)); } @@ -169,7 +169,7 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> { // remember it for future commands. socklen_t addr_size = sizeof(addr); ASSERT_THAT( - getsockname(socket_fd, reinterpret_cast<struct sockaddr *>(&addr), + getsockname(socket_fd, reinterpret_cast<struct sockaddr*>(&addr), &addr_size), SyscallSucceeds()); port_ = addr.sin_port; diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc index 2f9821555..3bf7081b9 100644 --- a/test/syscalls/linux/socket_inet_loopback.cc +++ b/test/syscalls/linux/socket_inet_loopback.cc @@ -828,6 +828,164 @@ TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) { EXPECT_EQ(get, kUserTimeout); } +// TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not +// saved. Enable S/R once issue is fixed. +TEST_P(SocketInetLoopbackTest, TCPDeferAccept_NoRandomSave) { + // TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not + // saved. Enable S/R issue is fixed. + DisableSave ds; + + auto const& param = GetParam(); + TestAddress const& listener = param.listener; + TestAddress const& connector = param.connector; + + // Create the listening socket. + const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE( + Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP)); + sockaddr_storage listen_addr = listener.addr; + ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr), + listener.addr_len), + SyscallSucceeds()); + ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds()); + + // Get the port bound by the listening socket. + socklen_t addrlen = listener.addr_len; + ASSERT_THAT(getsockname(listen_fd.get(), + reinterpret_cast<sockaddr*>(&listen_addr), &addrlen), + SyscallSucceeds()); + + const uint16_t port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); + + // Set the TCP_DEFER_ACCEPT on the listening socket. + constexpr int kTCPDeferAccept = 3; + ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, + &kTCPDeferAccept, sizeof(kTCPDeferAccept)), + SyscallSucceeds()); + + // Connect to the listening socket. + FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( + Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); + + sockaddr_storage conn_addr = connector.addr; + ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); + ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), + reinterpret_cast<sockaddr*>(&conn_addr), + connector.addr_len), + SyscallSucceeds()); + + // Set the listening socket to nonblock so that we can verify that there is no + // connection in queue despite the connect above succeeding since the peer has + // sent no data and TCP_DEFER_ACCEPT is set on the listening socket. Set the + // FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(listen_fd.get(), F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds()); + + ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Set FD back to blocking. + opts &= ~O_NONBLOCK; + ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds()); + + // Now write some data to the socket. + int data = 0; + ASSERT_THAT(RetryEINTR(write)(conn_fd.get(), &data, sizeof(data)), + SyscallSucceedsWithValue(sizeof(data))); + + // This should now cause the connection to complete and be delivered to the + // accept socket. + + // Accept the connection. + auto accepted = + ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); + + // Verify that the accepted socket returns the data written. + int get = -1; + ASSERT_THAT(RetryEINTR(recv)(accepted.get(), &get, sizeof(get), 0), + SyscallSucceedsWithValue(sizeof(get))); + + EXPECT_EQ(get, data); +} + +// TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not +// saved. Enable S/R once issue is fixed. +TEST_P(SocketInetLoopbackTest, TCPDeferAcceptTimeout_NoRandomSave) { + // TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not + // saved. Enable S/R once issue is fixed. + DisableSave ds; + + auto const& param = GetParam(); + TestAddress const& listener = param.listener; + TestAddress const& connector = param.connector; + + // Create the listening socket. + const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE( + Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP)); + sockaddr_storage listen_addr = listener.addr; + ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr), + listener.addr_len), + SyscallSucceeds()); + ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds()); + + // Get the port bound by the listening socket. + socklen_t addrlen = listener.addr_len; + ASSERT_THAT(getsockname(listen_fd.get(), + reinterpret_cast<sockaddr*>(&listen_addr), &addrlen), + SyscallSucceeds()); + + const uint16_t port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); + + // Set the TCP_DEFER_ACCEPT on the listening socket. + constexpr int kTCPDeferAccept = 3; + ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, + &kTCPDeferAccept, sizeof(kTCPDeferAccept)), + SyscallSucceeds()); + + // Connect to the listening socket. + FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( + Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); + + sockaddr_storage conn_addr = connector.addr; + ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); + ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), + reinterpret_cast<sockaddr*>(&conn_addr), + connector.addr_len), + SyscallSucceeds()); + + // Set the listening socket to nonblock so that we can verify that there is no + // connection in queue despite the connect above succeeding since the peer has + // sent no data and TCP_DEFER_ACCEPT is set on the listening socket. Set the + // FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(listen_fd.get(), F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds()); + + // Verify that there is no acceptable connection before TCP_DEFER_ACCEPT + // timeout is hit. + absl::SleepFor(absl::Seconds(kTCPDeferAccept - 1)); + ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Set FD back to blocking. + opts &= ~O_NONBLOCK; + ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds()); + + // Now sleep for a little over the TCP_DEFER_ACCEPT duration. When the timeout + // is hit a SYN-ACK should be retransmitted by the listener as a last ditch + // attempt to complete the connection with or without data. + absl::SleepFor(absl::Seconds(2)); + + // Verify that we have a connection that can be accepted even though no + // data was written. + auto accepted = + ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); +} + INSTANTIATE_TEST_SUITE_P( All, SocketInetLoopbackTest, ::testing::Values( diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc index 57ce8e169..27779e47c 100644 --- a/test/syscalls/linux/socket_ip_tcp_generic.cc +++ b/test/syscalls/linux/socket_ip_tcp_generic.cc @@ -24,6 +24,7 @@ #include <sys/un.h> #include "gtest/gtest.h" +#include "absl/memory/memory.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "test/syscalls/linux/socket_test_util.h" @@ -875,5 +876,37 @@ TEST_P(TCPSocketPairTest, SetTCPUserTimeoutAboveZero) { EXPECT_EQ(get, kAbove); } +TEST_P(TCPSocketPairTest, TCPResetDuringClose_NoRandomSave) { + DisableSave ds; // Too many syscalls. + constexpr int kThreadCount = 1000; + std::unique_ptr<ScopedThread> instances[kThreadCount]; + for (int i = 0; i < kThreadCount; i++) { + instances[i] = absl::make_unique<ScopedThread>([&]() { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ScopedThread t([&]() { + // Close one end to trigger sending of a FIN. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; + // Wait up to 20 seconds for the data. + constexpr int kPollTimeoutMs = 20000; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); + }); + + // Send some data then close. + constexpr char kStr[] = "abc"; + ASSERT_THAT(write(sockets->first_fd(), kStr, 3), + SyscallSucceedsWithValue(3)); + absl::SleepFor(absl::Milliseconds(10)); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + t.Join(); + }); + } + for (int i = 0; i < kThreadCount; i++) { + instances[i]->Join(); + } +} + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc index 405dbbd73..15d4b85a7 100644 --- a/test/syscalls/linux/socket_netdevice.cc +++ b/test/syscalls/linux/socket_netdevice.cc @@ -91,7 +91,7 @@ TEST(NetdeviceTest, Netmask) { int prefixlen = -1; ASSERT_NO_ERRNO(NetlinkRequestResponse( fd, &req, sizeof(req), - [&](const struct nlmsghdr *hdr) { + [&](const struct nlmsghdr* hdr) { EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE))); EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) @@ -107,8 +107,8 @@ TEST(NetdeviceTest, Netmask) { // RTM_NEWADDR contains at least the header and ifaddrmsg. EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg)); - struct ifaddrmsg *ifaddrmsg = - reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(hdr)); + struct ifaddrmsg* ifaddrmsg = + reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(hdr)); if (ifaddrmsg->ifa_index == static_cast<uint32_t>(ifr.ifr_ifindex) && ifaddrmsg->ifa_family == AF_INET) { prefixlen = ifaddrmsg->ifa_prefixlen; @@ -127,8 +127,8 @@ TEST(NetdeviceTest, Netmask) { snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); ASSERT_THAT(ioctl(sock.get(), SIOCGIFNETMASK, &ifr), SyscallSucceeds()); EXPECT_EQ(ifr.ifr_netmask.sa_family, AF_INET); - struct sockaddr_in *sin = - reinterpret_cast<struct sockaddr_in *>(&ifr.ifr_netmask); + struct sockaddr_in* sin = + reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask); EXPECT_EQ(sin->sin_addr.s_addr, mask); } diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index ef567f512..1e28e658d 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -442,6 +442,90 @@ TEST(NetlinkRouteTest, GetRouteDump) { EXPECT_TRUE(dstFound); } +// GetRouteRequest tests a RTM_GETROUTE request with RTM_F_LOOKUP_TABLE flag. +TEST(NetlinkRouteTest, GetRouteRequest) { + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); + uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); + + struct __attribute__((__packed__)) request { + struct nlmsghdr hdr; + struct rtmsg rtm; + struct nlattr nla; + struct in_addr sin_addr; + }; + + constexpr uint32_t kSeq = 12345; + + struct request req = {}; + req.hdr.nlmsg_len = sizeof(req); + req.hdr.nlmsg_type = RTM_GETROUTE; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = kSeq; + + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_dst_len = 32; + req.rtm.rtm_src_len = 0; + req.rtm.rtm_tos = 0; + req.rtm.rtm_table = RT_TABLE_UNSPEC; + req.rtm.rtm_protocol = RTPROT_UNSPEC; + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + req.rtm.rtm_type = RTN_UNSPEC; + req.rtm.rtm_flags = RTM_F_LOOKUP_TABLE; + + req.nla.nla_len = 8; + req.nla.nla_type = RTA_DST; + inet_aton("127.0.0.2", &req.sin_addr); + + bool rtDstFound = false; + ASSERT_NO_ERRNO(NetlinkRequestResponseSingle( + fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) { + // Validate the reponse to RTM_GETROUTE request with RTM_F_LOOKUP_TABLE + // flag. + EXPECT_THAT(hdr->nlmsg_type, RTM_NEWROUTE); + + EXPECT_TRUE(hdr->nlmsg_flags == 0) << std::hex << hdr->nlmsg_flags; + + EXPECT_EQ(hdr->nlmsg_seq, kSeq); + EXPECT_EQ(hdr->nlmsg_pid, port); + + // RTM_NEWROUTE contains at least the header and rtmsg. + ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct rtmsg))); + const struct rtmsg* msg = + reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(hdr)); + + // NOTE: rtmsg fields are char fields. + std::cout << "Found route table=" << static_cast<int>(msg->rtm_table) + << ", protocol=" << static_cast<int>(msg->rtm_protocol) + << ", scope=" << static_cast<int>(msg->rtm_scope) + << ", type=" << static_cast<int>(msg->rtm_type); + + EXPECT_EQ(msg->rtm_family, AF_INET); + EXPECT_EQ(msg->rtm_dst_len, 32); + EXPECT_TRUE((msg->rtm_flags & RTM_F_CLONED) == RTM_F_CLONED) + << std::hex << msg->rtm_flags; + + int len = RTM_PAYLOAD(hdr); + std::cout << ", len=" << len; + for (struct rtattr* attr = RTM_RTA(msg); RTA_OK(attr, len); + attr = RTA_NEXT(attr, len)) { + if (attr->rta_type == RTA_DST) { + char address[INET_ADDRSTRLEN] = {}; + inet_ntop(AF_INET, RTA_DATA(attr), address, sizeof(address)); + std::cout << ", dst=" << address; + rtDstFound = true; + } else if (attr->rta_type == RTA_OIF) { + const char* oif = reinterpret_cast<const char*>(RTA_DATA(attr)); + std::cout << ", oif=" << oif; + } + } + + std::cout << std::endl; + })); + // Found RTA_DST for RTM_F_LOOKUP_TABLE. + EXPECT_TRUE(rtDstFound); +} + // RecvmsgTrunc tests the recvmsg MSG_TRUNC flag with zero length output // buffer. MSG_TRUNC with a zero length buffer should consume subsequent // messages off the socket. diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc index 723f5d728..cd2212a1a 100644 --- a/test/syscalls/linux/socket_netlink_util.cc +++ b/test/syscalls/linux/socket_netlink_util.cc @@ -108,5 +108,43 @@ PosixError NetlinkRequestResponse( return NoError(); } +PosixError NetlinkRequestResponseSingle( + const FileDescriptor& fd, void* request, size_t len, + const std::function<void(const struct nlmsghdr* hdr)>& fn) { + struct iovec iov = {}; + iov.iov_base = request; + iov.iov_len = len; + + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + // No destination required; it defaults to pid 0, the kernel. + + RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(sendmsg)(fd.get(), &msg, 0)); + + constexpr size_t kBufferSize = 4096; + std::vector<char> buf(kBufferSize); + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + + int ret; + RETURN_ERROR_IF_SYSCALL_FAIL(ret = RetryEINTR(recvmsg)(fd.get(), &msg, 0)); + + // We don't bother with the complexity of dealing with truncated messages. + // We must allocate a large enough buffer up front. + if ((msg.msg_flags & MSG_TRUNC) == MSG_TRUNC) { + return PosixError( + EIO, + absl::StrCat("Received truncated message with flags: ", msg.msg_flags)); + } + + for (struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf.data()); + NLMSG_OK(hdr, ret); hdr = NLMSG_NEXT(hdr, ret)) { + fn(hdr); + } + + return NoError(); +} + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_util.h b/test/syscalls/linux/socket_netlink_util.h index 76e772c48..3678c0599 100644 --- a/test/syscalls/linux/socket_netlink_util.h +++ b/test/syscalls/linux/socket_netlink_util.h @@ -32,12 +32,21 @@ PosixErrorOr<FileDescriptor> NetlinkBoundSocket(int protocol); // Returns the port ID of the passed socket. PosixErrorOr<uint32_t> NetlinkPortID(int fd); -// Send the passed request and call fn will all response netlink messages. +// Send the passed request and call fn on all response netlink messages. +// +// To be used on requests with NLM_F_MULTI reponses. PosixError NetlinkRequestResponse( const FileDescriptor& fd, void* request, size_t len, const std::function<void(const struct nlmsghdr* hdr)>& fn, bool expect_nlmsgerr); +// Send the passed request and call fn on all response netlink messages. +// +// To be used on requests without NLM_F_MULTI reponses. +PosixError NetlinkRequestResponseSingle( + const FileDescriptor& fd, void* request, size_t len, + const std::function<void(const struct nlmsghdr* hdr)>& fn); + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_stream_blocking.cc b/test/syscalls/linux/socket_stream_blocking.cc index e9cc082bf..538ee2268 100644 --- a/test/syscalls/linux/socket_stream_blocking.cc +++ b/test/syscalls/linux/socket_stream_blocking.cc @@ -32,38 +32,38 @@ namespace gvisor { namespace testing { TEST_P(BlockingStreamSocketPairTest, BlockPartialWriteClosed) { - // FIXME(b/35921550): gVisor doesn't support SO_SNDBUF on UDS, nor does it - // enforce any limit; it will write arbitrary amounts of data without - // blocking. - SKIP_IF(IsRunningOnGvisor()); - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int buffer_size; - socklen_t length = sizeof(buffer_size); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, - &buffer_size, &length), - SyscallSucceeds()); - - int wfd = sockets->first_fd(); - ScopedThread t([wfd, buffer_size]() { - std::vector<char> buf(2 * buffer_size); - // Write more than fits in the buffer. Blocks then returns partial write - // when the other end is closed. The next call returns EPIPE. - // - // N.B. writes occur in chunks, so we may see less than buffer_size from - // the first call. - ASSERT_THAT(write(wfd, buf.data(), buf.size()), - SyscallSucceedsWithValue(::testing::Gt(0))); - ASSERT_THAT(write(wfd, buf.data(), buf.size()), - ::testing::AnyOf(SyscallFailsWithErrno(EPIPE), - SyscallFailsWithErrno(ECONNRESET))); - }); - - // Leave time for write to become blocked. - absl::SleepFor(absl::Seconds(1)); - - ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); + // FIXME(b/35921550): gVisor doesn't support SO_SNDBUF on UDS, nor does it + // enforce any limit; it will write arbitrary amounts of data without + // blocking. + SKIP_IF(IsRunningOnGvisor()); + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int buffer_size; + socklen_t length = sizeof(buffer_size); + ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, + &buffer_size, &length), + SyscallSucceeds()); + + int wfd = sockets->first_fd(); + ScopedThread t([wfd, buffer_size]() { + std::vector<char> buf(2 * buffer_size); + // Write more than fits in the buffer. Blocks then returns partial write + // when the other end is closed. The next call returns EPIPE. + // + // N.B. writes occur in chunks, so we may see less than buffer_size from + // the first call. + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(::testing::Gt(0))); + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + ::testing::AnyOf(SyscallFailsWithErrno(EPIPE), + SyscallFailsWithErrno(ECONNRESET))); + }); + + // Leave time for write to become blocked. + absl::SleepFor(absl::Seconds(1)); + + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); } // Random save may interrupt the call to sendmsg() in SendLargeSendMsg(), diff --git a/test/syscalls/linux/splice.cc b/test/syscalls/linux/splice.cc index 85232cb1f..faa1247f6 100644 --- a/test/syscalls/linux/splice.cc +++ b/test/syscalls/linux/splice.cc @@ -60,6 +60,62 @@ TEST(SpliceTest, TwoRegularFiles) { SyscallFailsWithErrno(EINVAL)); } +int memfd_create(const std::string& name, unsigned int flags) { + return syscall(__NR_memfd_create, name.c_str(), flags); +} + +TEST(SpliceTest, NegativeOffset) { + // Create a new pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + const FileDescriptor rfd(fds[0]); + const FileDescriptor wfd(fds[1]); + + // Fill the pipe. + std::vector<char> buf(kPageSize); + RandomizeBuffer(buf.data(), buf.size()); + ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(kPageSize)); + + // Open the output file as write only. + int fd; + EXPECT_THAT(fd = memfd_create("negative", 0), SyscallSucceeds()); + const FileDescriptor out_fd(fd); + + loff_t out_offset = 0xffffffffffffffffull; + constexpr int kSize = 2; + EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kSize, 0), + SyscallFailsWithErrno(EINVAL)); +} + +// Write offset + size overflows int64. +// +// This is a regression test for b/148041624. +TEST(SpliceTest, WriteOverflow) { + // Create a new pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + const FileDescriptor rfd(fds[0]); + const FileDescriptor wfd(fds[1]); + + // Fill the pipe. + std::vector<char> buf(kPageSize); + RandomizeBuffer(buf.data(), buf.size()); + ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(kPageSize)); + + // Open the output file. + int fd; + EXPECT_THAT(fd = memfd_create("overflow", 0), SyscallSucceeds()); + const FileDescriptor out_fd(fd); + + // out_offset + kSize overflows INT64_MAX. + loff_t out_offset = 0x7ffffffffffffffeull; + constexpr int kSize = 3; + EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kSize, 0), + SyscallFailsWithErrno(EINVAL)); +} + TEST(SpliceTest, SamePipe) { // Create a new pipe. int fds[2]; diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc index 30de2f8ff..388d75835 100644 --- a/test/syscalls/linux/stat.cc +++ b/test/syscalls/linux/stat.cc @@ -599,8 +599,8 @@ struct kernel_statx { uint64_t __spare2[14]; }; -int statx(int dirfd, const char *pathname, int flags, unsigned int mask, - struct kernel_statx *statxbuf) { +int statx(int dirfd, const char* pathname, int flags, unsigned int mask, + struct kernel_statx* statxbuf) { return syscall(SYS_statx, dirfd, pathname, flags, mask, statxbuf); } diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 33a5ac66c..525ccbd88 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -1286,6 +1286,59 @@ TEST_P(SimpleTcpSocketTest, SetTCPUserTimeout) { EXPECT_EQ(get, kTCPUserTimeout); } +TEST_P(SimpleTcpSocketTest, SetTCPDeferAcceptNeg) { + FileDescriptor s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + // -ve TCP_DEFER_ACCEPT is same as setting it to zero. + constexpr int kNeg = -1; + EXPECT_THAT( + setsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &kNeg, sizeof(kNeg)), + SyscallSucceeds()); + int get = -1; + socklen_t get_len = sizeof(get); + ASSERT_THAT( + getsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, 0); +} + +TEST_P(SimpleTcpSocketTest, GetTCPDeferAcceptDefault) { + FileDescriptor s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + int get = -1; + socklen_t get_len = sizeof(get); + ASSERT_THAT( + getsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, 0); +} + +TEST_P(SimpleTcpSocketTest, SetTCPDeferAcceptGreaterThanZero) { + FileDescriptor s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + // kTCPDeferAccept is in seconds. + // NOTE: linux translates seconds to # of retries and back from + // #of retries to seconds. Which means only certain values + // translate back exactly. That's why we use 3 here, a value of + // 5 will result in us getting back 7 instead of 5 in the + // getsockopt. + constexpr int kTCPDeferAccept = 3; + ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, + &kTCPDeferAccept, sizeof(kTCPDeferAccept)), + SyscallSucceeds()); + int get = -1; + socklen_t get_len = sizeof(get); + ASSERT_THAT( + getsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &get, &get_len), + SyscallSucceeds()); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kTCPDeferAccept); +} + INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest, ::testing::Values(AF_INET, AF_INET6)); diff --git a/test/syscalls/linux/udp_socket_errqueue_test_case.cc b/test/syscalls/linux/udp_socket_errqueue_test_case.cc index 147978f46..fcdba7279 100644 --- a/test/syscalls/linux/udp_socket_errqueue_test_case.cc +++ b/test/syscalls/linux/udp_socket_errqueue_test_case.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "test/syscalls/linux/udp_socket_test_cases.h" +#ifndef __fuchsia__ #include <arpa/inet.h> #include <fcntl.h> @@ -27,6 +27,7 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/udp_socket_test_cases.h" #include "test/syscalls/linux/unix_domain_socket_test_util.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" @@ -52,3 +53,5 @@ TEST_P(UdpSocketTest, ErrorQueue) { } // namespace testing } // namespace gvisor + +#endif // __fuchsia__ diff --git a/test/syscalls/linux/utimes.cc b/test/syscalls/linux/utimes.cc index 12b925a51..3a927a430 100644 --- a/test/syscalls/linux/utimes.cc +++ b/test/syscalls/linux/utimes.cc @@ -163,12 +163,12 @@ TEST(FutimesatTest, OnRelPath) { TEST(FutimesatTest, InvalidNsec) { auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); struct timeval times[4][2] = {{ - {0, 1}, // Valid + {0, 1}, // Valid {1, static_cast<int64_t>(1e7)} // Invalid }, { {1, static_cast<int64_t>(1e7)}, // Invalid - {0, 1} // Valid + {0, 1} // Valid }, { {0, 1}, // Valid @@ -288,14 +288,15 @@ TEST(UtimeTest, ZeroAtimeandMtime) { TEST(UtimensatTest, InvalidNsec) { auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - struct timespec times[2][2] = {{ - {0, UTIME_OMIT}, // Valid - {2, static_cast<int64_t>(1e10)} // Invalid - }, - { - {2, static_cast<int64_t>(1e10)}, // Invalid - {0, UTIME_OMIT} // Valid - }}; + struct timespec times[2][2] = { + { + {0, UTIME_OMIT}, // Valid + {2, static_cast<int64_t>(1e10)} // Invalid + }, + { + {2, static_cast<int64_t>(1e10)}, // Invalid + {0, UTIME_OMIT} // Valid + }}; for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) { std::cout << "test:" << i << "\n"; diff --git a/test/syscalls/linux/xattr.cc b/test/syscalls/linux/xattr.cc index e77c355d7..ab21d68c6 100644 --- a/test/syscalls/linux/xattr.cc +++ b/test/syscalls/linux/xattr.cc @@ -131,7 +131,7 @@ TEST_F(XattrTest, XattrWriteOnly_NoRandomSave) { } TEST_F(XattrTest, XattrTrustedWithNonadmin) { - // TODO(b/127675828): Support setxattr and getxattr with "trusted" prefix. + // TODO(b/148380782): Support setxattr and getxattr with "trusted" prefix. SKIP_IF(IsRunningOnGvisor()); SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); diff --git a/test/uds/BUILD b/test/uds/BUILD index a3843e699..51e2c7ce8 100644 --- a/test/uds/BUILD +++ b/test/uds/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//tools:defs.bzl", "go_library") package( default_visibility = ["//:sandbox"], @@ -9,7 +9,6 @@ go_library( name = "uds", testonly = 1, srcs = ["uds.go"], - importpath = "gvisor.dev/gvisor/test/uds", deps = [ "//pkg/log", "//pkg/unet", diff --git a/test/util/BUILD b/test/util/BUILD index cbc728159..1ac8b3fd6 100644 --- a/test/util/BUILD +++ b/test/util/BUILD @@ -1,5 +1,4 @@ -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") -load("//test/syscalls:build_defs.bzl", "select_for_linux") +load("//tools:defs.bzl", "cc_library", "cc_test", "select_system") package( default_visibility = ["//:sandbox"], @@ -142,12 +141,13 @@ cc_library( cc_library( name = "save_util", testonly = 1, - srcs = ["save_util.cc"] + - select_for_linux( - ["save_util_linux.cc"], - ["save_util_other.cc"], - ), + srcs = [ + "save_util.cc", + "save_util_linux.cc", + "save_util_other.cc", + ], hdrs = ["save_util.h"], + defines = select_system(), ) cc_library( @@ -166,6 +166,14 @@ cc_library( ) cc_library( + name = "platform_util", + testonly = 1, + srcs = ["platform_util.cc"], + hdrs = ["platform_util.h"], + deps = [":test_util"], +) + +cc_library( name = "posix_error", testonly = 1, srcs = ["posix_error.cc"], @@ -234,13 +242,11 @@ cc_library( testonly = 1, srcs = [ "test_util.cc", - ] + select_for_linux( - [ - "test_util_impl.cc", - "test_util_runfiles.cc", - ], - ), + "test_util_impl.cc", + "test_util_runfiles.cc", + ], hdrs = ["test_util.h"], + defines = select_system(), deps = [ ":fs_util", ":logging", diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc index 5d733887b..9fee52fbb 100644 --- a/test/util/capability_util.cc +++ b/test/util/capability_util.cc @@ -36,10 +36,10 @@ PosixErrorOr<bool> CanCreateUserNamespace() { ASSIGN_OR_RETURN_ERRNO( auto child_stack, MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - int const child_pid = - clone(+[](void*) { return 0; }, - reinterpret_cast<void*>(child_stack.addr() + kPageSize), - CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr); + int const child_pid = clone( + +[](void*) { return 0; }, + reinterpret_cast<void*>(child_stack.addr() + kPageSize), + CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr); if (child_pid > 0) { int status; int const ret = waitpid(child_pid, &status, /* options = */ 0); diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc index 042cec94a..052781445 100644 --- a/test/util/fs_util.cc +++ b/test/util/fs_util.cc @@ -452,7 +452,7 @@ PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename, std::string CleanPath(const absl::string_view unclean_path) { std::string path = std::string(unclean_path); - const char *src = path.c_str(); + const char* src = path.c_str(); std::string::iterator dst = path.begin(); // Check for absolute path and determine initial backtrack limit. diff --git a/test/util/mount_util.h b/test/util/mount_util.h index 484de560e..09e2281eb 100644 --- a/test/util/mount_util.h +++ b/test/util/mount_util.h @@ -31,10 +31,10 @@ namespace testing { // Mount mounts the filesystem, and unmounts when the returned reference is // destroyed. -inline PosixErrorOr<Cleanup> Mount(const std::string &source, - const std::string &target, - const std::string &fstype, uint64_t mountflags, - const std::string &data, +inline PosixErrorOr<Cleanup> Mount(const std::string& source, + const std::string& target, + const std::string& fstype, + uint64_t mountflags, const std::string& data, uint64_t umountflags) { if (mount(source.c_str(), target.c_str(), fstype.c_str(), mountflags, data.c_str()) == -1) { diff --git a/test/util/multiprocess_util.h b/test/util/multiprocess_util.h index 61526b4e7..2f3bf4a6f 100644 --- a/test/util/multiprocess_util.h +++ b/test/util/multiprocess_util.h @@ -99,11 +99,13 @@ inline PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, const ExecveArray& argv, const ExecveArray& envv, pid_t* child, int* execve_errno) { - return ForkAndExec(filename, argv, envv, [] {}, child, execve_errno); + return ForkAndExec( + filename, argv, envv, [] {}, child, execve_errno); } // Equivalent to ForkAndExec, except using dirfd and flags with execveat. -PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, const std::string& pathname, +PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, + const std::string& pathname, const ExecveArray& argv, const ExecveArray& envv, int flags, const std::function<void()>& fn, diff --git a/test/util/platform_util.cc b/test/util/platform_util.cc new file mode 100644 index 000000000..2724e63f3 --- /dev/null +++ b/test/util/platform_util.cc @@ -0,0 +1,49 @@ +// Copyright 2020 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 "test/util/platform_util.h" + +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +PlatformSupport PlatformSupport32Bit() { + if (GvisorPlatform() == Platform::kPtrace) { + return PlatformSupport::NotSupported; + } else if (GvisorPlatform() == Platform::kKVM) { + return PlatformSupport::Segfault; + } else { + return PlatformSupport::Allowed; + } +} + +PlatformSupport PlatformSupportAlignmentCheck() { + return PlatformSupport::Allowed; +} + +PlatformSupport PlatformSupportMultiProcess() { + return PlatformSupport::Allowed; +} + +PlatformSupport PlatformSupportInt3() { + if (GvisorPlatform() == Platform::kKVM) { + return PlatformSupport::NotSupported; + } else { + return PlatformSupport::Allowed; + } +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/platform_util.h b/test/util/platform_util.h new file mode 100644 index 000000000..28cc92371 --- /dev/null +++ b/test/util/platform_util.h @@ -0,0 +1,56 @@ +// Copyright 2020 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. + +#ifndef GVISOR_TEST_UTIL_PLATFORM_UTIL_H_ +#define GVISOR_TEST_UTIL_PLATFORM_UTIL_H_ + +namespace gvisor { +namespace testing { + +// PlatformSupport is a generic enumeration of classes of support. +// +// It is up to the individual functions and callers to agree on the precise +// definition for each case. The document here generally refers to 32-bit +// as an example. Many cases will use only NotSupported and Allowed. +enum class PlatformSupport { + // The feature is not supported on the current platform. + // + // In the case of 32-bit, this means that calls will generally be interpreted + // as 64-bit calls, and there is no support for 32-bit binaries, long calls, + // etc. This usually means that the underlying implementation just pretends + // that 32-bit doesn't exist. + NotSupported, + + // Calls will be ignored by the kernel with a fixed error. + Ignored, + + // Calls will result in a SIGSEGV or similar fault. + Segfault, + + // The feature is supported as expected. + // + // In the case of 32-bit, this means that the system call or far call will be + // handled properly. + Allowed, +}; + +PlatformSupport PlatformSupport32Bit(); +PlatformSupport PlatformSupportAlignmentCheck(); +PlatformSupport PlatformSupportMultiProcess(); +PlatformSupport PlatformSupportInt3(); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_PLATFORM_UTL_H_ diff --git a/test/util/save_util_linux.cc b/test/util/save_util_linux.cc index cd56118c0..d0aea8e6a 100644 --- a/test/util/save_util_linux.cc +++ b/test/util/save_util_linux.cc @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifdef __linux__ + #include <errno.h> #include <sys/syscall.h> #include <unistd.h> @@ -43,3 +45,5 @@ void MaybeSave() { } // namespace testing } // namespace gvisor + +#endif diff --git a/test/util/save_util_other.cc b/test/util/save_util_other.cc index 1aca663b7..931af2c29 100644 --- a/test/util/save_util_other.cc +++ b/test/util/save_util_other.cc @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef __linux__ + namespace gvisor { namespace testing { @@ -21,3 +23,5 @@ void MaybeSave() { } // namespace testing } // namespace gvisor + +#endif diff --git a/test/util/test_util.cc b/test/util/test_util.cc index 848504c88..15cbc6da6 100644 --- a/test/util/test_util.cc +++ b/test/util/test_util.cc @@ -45,20 +45,13 @@ namespace testing { bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; } -Platform GvisorPlatform() { +const std::string GvisorPlatform() { // Set by runner.go. char* env = getenv(TEST_ON_GVISOR); if (!env) { return Platform::kNative; } - if (strcmp(env, "ptrace") == 0) { - return Platform::kPtrace; - } - if (strcmp(env, "kvm") == 0) { - return Platform::kKVM; - } - std::cerr << "unknown platform " << env; - abort(); + return std::string(env); } bool IsRunningWithHostinet() { diff --git a/test/util/test_util.h b/test/util/test_util.h index b3235c7e3..2d22b0eb8 100644 --- a/test/util/test_util.h +++ b/test/util/test_util.h @@ -26,16 +26,13 @@ // IsRunningOnGvisor returns true if the test is known to be running on gVisor. // GvisorPlatform can be used to get more detail: // -// switch (GvisorPlatform()) { -// case Platform::kNative: -// case Platform::kGvisor: -// EXPECT_THAT(mmap(...), SyscallSucceeds()); -// break; -// case Platform::kPtrace: -// EXPECT_THAT(mmap(...), SyscallFailsWithErrno(ENOSYS)); -// break; +// if (GvisorPlatform() == Platform::kPtrace) { +// ... // } // +// SetupGvisorDeathTest ensures that signal handling does not interfere with +/// tests that rely on fatal signals. +// // Matchers // ======== // @@ -213,13 +210,15 @@ void TestInit(int* argc, char*** argv); if (expr) GTEST_SKIP() << #expr; \ } while (0) -enum class Platform { - kNative, - kKVM, - kPtrace, -}; +// Platform contains platform names. +namespace Platform { +constexpr char kNative[] = "native"; +constexpr char kPtrace[] = "ptrace"; +constexpr char kKVM[] = "kvm"; +} // namespace Platform + bool IsRunningOnGvisor(); -Platform GvisorPlatform(); +const std::string GvisorPlatform(); bool IsRunningWithHostinet(); #ifdef __linux__ diff --git a/test/util/test_util_runfiles.cc b/test/util/test_util_runfiles.cc index 7210094eb..694d21692 100644 --- a/test/util/test_util_runfiles.cc +++ b/test/util/test_util_runfiles.cc @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef __fuchsia__ + #include <iostream> #include <string> @@ -44,3 +46,5 @@ std::string RunfilePath(std::string path) { } // namespace testing } // namespace gvisor + +#endif // __fuchsia__ |